版权声明:本文为HCG原创文章,未经博主允许不得转载。请联系[email protected] https://blog.csdn.net/qq_39455116/article/details/83744584
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);
}
}
最后附上项目地址