六、自定义Mapper
业务Mapper接口PorscheMappr通过继承Mapper<T>接口从而获取了一系列的方法,这一系列的方法也不是Mapper<T>接口本身就有的,而是通过继承其他Mapper如BaseMapper<T>、ExampleMapper<T>等,而这些BaseMapper<T>又继承简介继承了SelectOneMapper<T>才获得selectOne方法,因此我们根据实际需要对Mapper<T>进行定制。
6.1 实现自定义Mapper
在common-mapper项目中新建一个common包,用来存放自定义的Mapper<T>,新建CustMapper<T>
public interface CustMapper<T> extends SelectOneMapper<T> { } 复制代码
这里也可以选择继承多个如SelectAllMapper<T>等。
在Spring配置文件application.xml中配置自定义的CustMapper<T>
<bean id="mapperScannerConfigurer" class="tk.mybatis.spring.mapper.MapperScannerConfigurer"> <!--指定接口所在的包--> <property name="basePackage" value="com.citi.mapper"></property> <!--配置自定义的CustMapper<T>接口--> <property name="properties"> <value> mappers=com.citi.common.CustMapper </value> </property> </bean> 复制代码
修改TeacherMapper的继承关系,改为继承自定义的CustMapper<T>接口
public interface TeacherMapper extends CustMapper<Teacher> { } 复制代码
增加TeacherMapper的测试类TeacherMapperTest,对继承来的selectOne方法进行测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:application.xml") public class TeacherMapperTest { @Resource private TeacherMapper teacherMapper; @Test public void selectOne(){ // 构造查询条件 Teacher record = new Teacher(); record.setGrade("三年二班"); record.setName("stark"); Teacher teacher = teacherMapper.selectOne(record); System.out.println("查询到的内容为:" + teacher); } } 复制代码
执行测试
成功输出根据查询条件查到的数据
plus:自定义的Mapper和普通的XxxMapper接口不能放在同一个包下,会导致Spring容器创建自定义Mapper的Bean失败
七、通用Mapper扩展
扩展指的是增加通用Mapper没有的功能,通用Mapper提供了一些列基本的增删改查以及条件查询主键查询等方法,但是没有提供批量操作的方法,官网中给出了扩展通用Mapper的例子即扩展批量插入的功能。
7.1 扩展实现批量更新
自定义的Mapper扩展可以参考官方已有的代码。
仿照官方的UpdateByPrimaryKeyMapper<T>接口来写我们自己的BatchUpdateMapper<T>接口
@RegisterMapper public interface BatchUpdateMapper<T> { @UpdateProvider(type = BatchUpdateProvider.class, method = "dynamicSQL") void batchUpdate(List<T> tList); } 复制代码
在common包下新建一个BatchUpdateProvider
仿照官方的BaseUpdateProvider实现自定义的BatchUpdateProvider,首先声明一个构造器
public class BatchUpdateProvider extends MapperTemplate { // 构造器 public BatchUpdateProvider(Class<?> mapperClass, MapperHelper mapperHelper) { super(mapperClass, mapperHelper); } } 复制代码
批量更新的SQL语句在Mapper XML文件中写入如下格式,将多条UPDATE SQL语句通过“;”连接起来执行
<foreach item="record" collection="list" separator=";"> UPDATE porsche <set> por_name = #{porsche.porName}, por_price = #{porsche.porPrice}, por_stock = #{porsche.porStock}, </set> por_id = #{porsche.por_id} </foreach> 复制代码
这就是我们要拼接的SQL语句,BatchUpdateProvider类中增加一个方法batchUpdate,该方法返回一个String即要执行的SQL语句
public String batchUpdate(MappedStatement ms){ // 1.新建一条SQL语句 StringBuilder sql = new StringBuilder(); // 2.拼接 foreach标签开头 sql.append("<foreach item="record" collection="list" separator=";" >"); // 获取实体类对象 Class<?> entityClass = getEntityClass(ms); String updateClause = SqlHelper.updateTable(entityClass,tableName(entityClass)); // 3.拼接 "UPDATE 表名 " sql.append(updateClause); // 4.拼接set标签开头 sql.append("<set>"); // 获取字段名称的集合 Set<EntityColumn> columns = EntityHelper.getColumns(entityClass); // 声明主键字段 String idColumn = null; String idColumnHolder = null; // 遍历字段 for (EntityColumn entityColumn : columns) { // 判断是否是主键,是主键要放在WHERE子句后面 boolean isPrimaryKey = entityColumn.isId(); System.out.println(isPrimaryKey); if (isPrimaryKey){ idColumn = entityColumn.getColumn(); System.out.println(idColumn); idColumnHolder = entityColumn.getColumnHolder("record"); } else { // 返回类似如下字符串"(实体类.属性,jdbcType=NUMERIC,typeHandler=MyTypeHandler)" // 获取字段名和属性名 String column = entityColumn.getColumn(); String columnHolder = entityColumn.getColumnHolder("record"); // 5.拼接por_name = #{porsche.porName}, sql.append(column).append("=").append(columnHolder).append(","); } } // 6.拼接set标签结尾 sql.append("</set>"); // 7.拼接where子句 sql.append("where ").append(idColumn).append("=").append(idColumnHolder); // 8.拼接 foreach标签结尾 sql.append("</foreach>"); // 9.返回SQL语句 return sql.toString(); } 复制代码
代码中拼接批量更新SQL的步骤为:
- 新建一条SQL语句
- 拼接 foreach标签开头
- 拼接 "UPDATE porsche "
- 拼接 set标签开头
- 拼接 por_name = #{porsche.porName}
- 拼接 set标签结尾
- 拼接 where子句
- 拼接 foreach标签结尾
- 返回SQL语句
然后让自定义的CustMapper继承自定义的BatchUpdateMapper
public interface CustMapper<T> extends SelectOneMapper<T>,BatchUpdateMapper<T> { } 复制代码
因为TeacherMapper接口继承了CustMapper,所有TeacherMapper接口就自动获得了batchUpdate方法,在TeacherMapperTest测试类中增加对batchUpdate的测试
@Test public void batchUpdate(){ List<Teacher> teacherList = new ArrayList<>(); for (int i = 7; i < 10; i++) { Teacher teacher = new Teacher(); teacher.setId(i); teacher.setGrade("五年" + i + "班"); teacher.setName("Ultron " + i); teacher.setAddress("New York"); teacher.setBirthDate(new Date()); teacherList.add(teacher); } teacherMapper.batchUpdate(teacherList); } 复制代码
执行测试前需要注意因为更新语句是将多条SQL语句通过“;”连接起来一次执行,所有需要在db.properties中jdbc_url后面要加上“&allowMultiQueries=true“。
执行测试
这里出现错误,根据输出的SQL语句判断应该是isId()方法没有判断出id是主键,查看Teacher实体类,发现id属性上没有增加@Id注解,也就是说通用Mapper并不知道id属性对应的字段是主键,也就没有做出正确的判断,导致输出控制台的错误语句。 在id属性上增加@Id注解以及@GeneratedValue注解
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; 复制代码
再次执行测试
数据库被成功修改。
自定义的批量更新扩展生效。