1. 程式人生 > >Springboot整合通用mapper、XML、service《spring boot學習五》

Springboot整合通用mapper、XML、service《spring boot學習五》

1. springmvc之mapper.xml的痛

​ 一般情況下都是一個類寫一個xml或者說即使N個類共用一個XML,其實對於開發者的工作量也是很大的,前期倒沒有什麼,因為可以用自動生成工具來生成,但是後期,如果要新增什麼欄位或者修改欄位的話,對於我們來說真的太噁心了

​ 所以能不能有一個共用的方法,也就是共用的mapper或者service等讓開發者少一些開發量,我們來自己封裝一個共用的mapper mapper.xml service serviceImpl

1.  封裝過程是基於物件中新增自定義註解,然後通過反射解析Class動態生成sql實現,
		不要和Mybatis API的SqlSession中的方法混淆,
2.  並且SpringBoot整合Mybatis後不要輕易使用SqlFactory和SqlSession,
		原因可參看Mybatis API
	   (http://www.mybatis.org/spring/zh/using-api.html),如下:

3.  它不會參與到 Spring 的事務之中。
		如果 SqlSession 使用 DataSource,它也會被 Spring 事務管理器使用,
			而且當前 有事務在進行時,這段程式碼會丟擲異常。
4.  MyBatis 的 DefaultSqlSession 是執行緒不安全的。
            如果在 bean 中注入了它,就會 發生錯誤。
            使用 DefaultSqlSession 建立的對映器也不是執行緒安全的。
            如果你將它們注入到 bean 中,是會發生錯誤的。
            你必須保證在 finally 塊中來關閉 SqlSession。

專案地址:傳送門

2. 確定這些需求之後,我們開始設計資料庫
CREATE TABLE `mapper` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(20)   NOT NULL,
  `sex` int(1) ,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;


重點來了,既然mapper.xml都想共用,那麼我們需要在傳參的時候就指定資料庫名稱還有每個成員變數對應得欄位,那這些放到哪裡合適呢?

答案:在每個實體類中指定

//注意:
//    使用註解編寫物件類@Table為表名,@Id為主鍵,@Column為列名,
//    注意@Id和@Column需要定義在get方法上。

@Table(value = "mapper")
//表的名稱
public class MapperDO {

//    使用註解編寫物件類@Table為表名,@Id為主鍵,@Column為列名,
//    注意@Id和@Column需要定義在get方法上。
    private Integer id ;

    private  String name ;

    private Integer sex ;

    @Id(value = "ID")
    public Integer getId() {
        return id;
    }


    public void setId(Integer id) {
        this.id = id;
    }
    @Column(value = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @Column(value = "sex")
    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "MapperDO{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex=" + sex +
                '}';
    }
}

3. 設計共用的mapper
public interface BaseMapper {
    int insert(Map params);

    int update(Map params);

    int delete(Map params);

    HashMap queryForObject(Map params);
}
4. 設計共用的mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.pf.org.cms.hcg.system.mapper.BaseMapper">


    <insert id="insert" parameterType="java.util.HashMap">
        INSERT INTO ${TABLE_NAME} (
        <foreach collection="COLUMNS" item="item" index="index" separator=",">
            ${item}
        </foreach>
        ) values (
        <foreach collection="VALUES" item="item" index="index" separator=",">
            #{item}
        </foreach>
        )
    </insert>

    <select id="queryForObject" parameterType="java.util.HashMap" resultType="java.util.HashMap">
        SELECT * FROM ${TABLE_NAME} WHERE ${KEY_ID} = #{KEY_VALUE}
    </select>

    <update id="update" parameterType="java.util.HashMap">
        UPDATE ${TABLE_NAME} SET
        <foreach collection="DATA" item="item" index="index" separator=",">
            ${item.COLUMN} = #{item.COL_VALUE}
        </foreach>
        WHERE ${KEY_ID} = #{KEY_VALUE}
    </update>

    <delete id="delete" parameterType="java.util.HashMap">
        DELETE FROM ${TABLE_NAME} WHERE ${KEY_ID} = #{KEY_VALUE}
    </delete>
</mapper>
5. 設計共用的service
public interface BaseServiceClient {
    /**
     * 儲存
     *
     * @param obj
     * @return
     */
    public int insert(Object obj);

    /**
     * 根據long型主鍵查詢
     *
     * @param id
     * @param c
     * @return
     */
    public HashMap query(long id, Class c);

    /**
     * 根據主鍵更新
     *
     * @param obj
     * @return
     */
    public int update(Object obj);

    /**
     * 根據主鍵刪除
     *
     * @param obj
     * @return
     */
    public int delete(Object obj);

    /**
     * 通用批量儲存
     *
     * @param sqlId
     * @param data
     * @return
     */
    public int batchInsert(String sqlId, List data);

}
6.設計共用的serviceImpl
@Service(value = "baseServie")
public class BaseServiceClientImpl implements BaseServiceClient {
    //分批儲存閥值
    private int count = 1000;

    private static final Logger log = LoggerFactory.getLogger(BaseServiceClientImpl.class);

    @Autowired
    private BaseMapper baseMapper;

    @Autowired
    SqlSessionTemplate sqlSessionTemplate;

    private Map<String, Object> transformObj(Object t, String type) {
        //獲取表名
        if (null == t.getClass().getAnnotation(Table.class)) {
            throw new CmsException("Error Input Object! Error @Table Annotation.");
        }
        Map<String, Object> re = new HashMap<String, Object>();
        re.put("TABLE_NAME", t.getClass().getAnnotation(Table.class).value());

        Method[] m = t.getClass().getMethods();
        if (null == m || m.length <= 0) {
            throw new CmsException("Error Input Object! No Method.");
        }
        //insert資料結構
        if ("insert".equals(type)) {
            List k = new ArrayList();//存放列名
            List v = new ArrayList();//存放列值
            for (Method i : m) {
                //獲取列名和值
                if (null != i.getAnnotation(Column.class)) {
                    k.add(i.getAnnotation(Column.class).value());
                    v.add(getInvokeValue(t, i));
                    continue;
                }
                if (null != i.getAnnotation(Id.class)) {
                    re.put("KEY_ID", i.getAnnotation(Id.class).value());
                    re.put("KEY_VALUE", getInvokeValue(t, i));
                }
            }
            if (k.size() != v.size()) {
                throw new CmsException("Error Input Object! Internal Error.");
            }
            re.put("COLUMNS", k);
            re.put("VALUES", v);
        } else if ("update".equals(type)) {
            //update資料結構
            List d = new ArrayList();
            for (Method i : m) {
                Map<String, Object> map = new HashMap<>();
                if (null != i.getAnnotation(Column.class) && null != getInvokeValue(t, i)) {
                    map.put("COLUMN", i.getAnnotation(Column.class).value());
                    map.put("COL_VALUE", getInvokeValue(t, i));
                    d.add(map);
                    continue;
                }
                if (null != i.getAnnotation(Id.class)) {
                    re.put("KEY_ID", i.getAnnotation(Id.class).value());
                    re.put("KEY_VALUE", getInvokeValue(t, i));
                }
            }
            re.put("DATA", d);
        } else if ("common".equals(type)) {
            //common資料結構
            for (Method i : m) {
                if (null != i.getAnnotation(Id.class)) {
                    re.put("KEY_ID", i.getAnnotation(Id.class).value());
                    re.put("KEY_VALUE", getInvokeValue(t, i));
                }
            }
        }
        return re;
    }

    private Object getInvokeValue(Object t, Method i) {
        try {
            return i.invoke(t, null);
        } catch (IllegalAccessException e) {
            throw new CmsException("Error Input Object! Error Invoke Get Method.", e);
        } catch (InvocationTargetException e) {
            throw new CmsException("Error Input Object! Error Invoke Get Method.", e);
        }
    }

    @Override
    public int insert(Object obj) {
        Map<String, Object> params = transformObj(obj, "insert");
        log.info(new StringBuffer("Function Insert.Transformed Params:").append(params).toString());
        return baseMapper.insert(params);
    }

    @Override
    public HashMap query(long id, Class c) {
        Map<String, Object> params = new HashMap<>();
        try {
            params = transformObj(c.newInstance(), "common");
            log.info(new StringBuffer("Function Query.Transformed Params:").append(params).toString());
        } catch (InstantiationException e) {
            throw new CmsException("Common Query Error.", e);
        } catch (IllegalAccessException e) {
            throw new CmsException("Common Query Error.", e);
        }
        params.put("KEY_VALUE", id);
        return baseMapper.queryForObject(params);
    }

    @Override
    public int update(Object obj) {
        Map<String, Object> params = transformObj(obj, "update");
        log.info(new StringBuffer("Function Update.Transformed Params:").append(params).toString());
        return baseMapper.update(params);
    }

    @Override
    public int delete(Object obj) {
        Map<String, Object> params = transformObj(obj, "common");
        log.info(new StringBuffer("Function Delete.Transformed Params:").append(params).toString());
        return baseMapper.delete(params);
    }

    @Override
    public int batchInsert(String sqlId, List data) {
        /*通用批量儲存,每count條分批commit*/
        if (data.isEmpty() || StringUtils.isEmpty(sqlId)) {
            log.info("批量儲存出錯,mapperIndex或data為空");
            return 0;
        }
        int result = 0, index = 0;
        SqlSession sqlSession = this.sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH);
        int lastIndex = count;
        try {
            while (index < data.size()) {
                if (data.size() <= lastIndex) {
                    lastIndex = data.size();
                    result = result + sqlSession.insert(sqlId, data.subList(index, lastIndex));
                    sqlSession.commit();
                    log.info(new StringBuilder("已儲存[").append(index).append("--").append(lastIndex - 1).append("}條資料").toString());
                    // 批量儲存結束,退出迴圈
                    break;
                } else {
                    result = result + sqlSession.insert(sqlId, data.subList(index, lastIndex));
                    sqlSession.commit();
                    log.info(new StringBuilder("已儲存[").append(index).append("--").append(lastIndex - 1).append("}條資料").toString());
                    // 設定下一批
                    index = lastIndex;
                    lastIndex = lastIndex + count;
                }
            }
        } catch (Exception e) {
            if (null != sqlSession) {
                sqlSession.rollback();
            }
            throw new CmsException(e);
        } finally {
            if (null != sqlSession) {
                sqlSession.close();
            }
        }
        return result;
    }

}
7. 當我們設計好這些共用的東西之後,我們怎麼用呢?

其實就是新建一個service和一個serviceImpl裡面把BaseService引用過來就行了

    @Autowired
    private BaseServiceClient baseServiceClient;

這個必須有哈,不能啥都省略了

8. 新建我們的service
public interface MapperService {
    public void add(MapperDO mapperDO) ;
}
9. 新建serviceImpl
@Service(value = "mapperCommonService")
public class MapperServiceImpl implements MapperService {

    @Autowired
    private BaseServiceClient baseServiceClient;
    @Override
    public void add(MapperDO mapperDO) {
        baseServiceClient.insert(mapperDO);
    }
}
10. 最後我們用一個測試用例試一下
@RunWith(SpringRunner.class)
@SpringBootTest
public class Common {

    @Autowired
    MapperService mapperService;

    @Test
    public void addUserInfos() throws Exception {
        MapperDO mapperDO = new MapperDO();
        //不用設定ID,因為我們資料庫自增了
        mapperDO.setName("小22米");
        mapperDO.setSex(2);
        //雖然我們資料庫sex欄位預設為0,但是這裡如果不設定的話會報錯,
        //問題暫時沒有解決
        mapperService.add(mapperDO);
    }
}
最後附上專案地址

傳送門