六、自定义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>
代码语言:javascript复制public interface CustMapper<T> extends SelectOneMapper<T> {
}
这里也可以选择继承多个如SelectAllMapper<T>等。
在Spring配置文件application.xml中配置自定义的CustMapper<T>
代码语言:javascript复制<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>接口
代码语言:javascript复制public interface TeacherMapper extends CustMapper<Teacher> {
}
增加TeacherMapper的测试类TeacherMapperTest,对继承来的selectOne方法进行测试
代码语言:javascript复制@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>接口
代码语言:javascript复制@RegisterMapper
public interface BatchUpdateMapper<T> {
@UpdateProvider(type = BatchUpdateProvider.class, method = "dynamicSQL")
void batchUpdate(List<T> tList);
}
在common包下新建一个BatchUpdateProvider
仿照官方的BaseUpdateProvider实现自定义的BatchUpdateProvider,首先声明一个构造器
代码语言:javascript复制public class BatchUpdateProvider extends MapperTemplate {
// 构造器
public BatchUpdateProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
}
批量更新的SQL语句在Mapper XML文件中写入如下格式,将多条UPDATE SQL语句通过“;”连接起来执行
代码语言:javascript复制<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语句
代码语言:javascript复制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
代码语言:javascript复制public interface CustMapper<T> extends SelectOneMapper<T>,BatchUpdateMapper<T> {
}
因为TeacherMapper接口继承了CustMapper,所有TeacherMapper接口就自动获得了batchUpdate方法,在TeacherMapperTest测试类中增加对batchUpdate的测试
代码语言:javascript复制@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注解
代码语言:javascript复制@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
再次执行测试
数据库被成功修改。
自定义的批量更新扩展生效。