mybatis实现零配置

基于 com.github.abel533-mapper改造的实现的动态查询字段,mapper已经实现了单表的操作不需要写配置文件,但是唯一的不足就是查询的时候不能根据具体的业务区查询指定的字段,默认是查询所有的字段,这样在数据访问量大的情况下性能会跟不上。几天基于这种物配制的基础,在原有的基础上修改了查询方便实现了根据业务查询指定的字段,实现方法很简单,在执行sql之前把需要查询的字段和本地线程绑定,然后在正真构造sql的时候用根本的线程的数据替换掉默认的所以字段,具体实现如下:

用自己的接口掩盖掉默认的接口(大多数是copy,因为默认接口是泛型,所以如果直接继承会有很多问题需要处理,所以这里是直接copy整个接口的代码,并添加了一个接口)

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.github.liuzw.mapper.Mapper;  
  2.   
  3. import java.util.List;  
  4.   
  5. import org.apache.ibatis.annotations.DeleteProvider;  
  6. import org.apache.ibatis.annotations.InsertProvider;  
  7. import org.apache.ibatis.annotations.Param;  
  8. import org.apache.ibatis.annotations.SelectProvider;  
  9. import org.apache.ibatis.annotations.UpdateProvider;  
  10.   
  11. /**  
  12.  * 改写默认的Mapper接口,新增根据id批量删除接口  
  13.  *   
  14.  * @author good-zhiwei 刘志伟  
  15.  * @data 2016年7月10日 下午12:41:00  
  16.  * @param <T>  
  17.  */  
  18. public interface LiuzwMapper<T> {  
  19.   
  20.     /**  
  21.      * 根据实体类不为null的字段进行查询,条件全部使用=号and条件  
  22.      *   
  23.      * @param record  
  24.      * @return  
  25.      */  
  26.     @SelectProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  27.     List<T> select(T record);  
  28.   
  29.     /**  
  30.      * 根据实体类不为null的字段查询总数,条件全部使用=号and条件  
  31.      *   
  32.      * @param record  
  33.      * @return  
  34.      */  
  35.     @SelectProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  36.     int selectCount(T record);  
  37.   
  38.     /**  
  39.      * 根据主键进行查询,必须保证结果唯一 单个字段做主键时,可以直接写主键的值 联合主键时,key可以是实体类,也可以是Map  
  40.      *   
  41.      * @param key  
  42.      * @return  
  43.      */  
  44.     @SelectProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  45.     T selectByPrimaryKey(Object key);  
  46.   
  47.     /**  
  48.      * 插入一条数据 支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写) 优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长  
  49.      *   
  50.      * @param record  
  51.      * @return  
  52.      */  
  53.     @InsertProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  54.     int insert(T record);  
  55.   
  56.     /**  
  57.      * 插入一条数据,只插入不为null的字段,不会影响有默认值的字段 支持Oracle序列,UUID,类似Mysql的INDENTITY自动增长(自动回写)  
  58.      * 优先使用传入的参数值,参数值空时,才会使用序列、UUID,自动增长  
  59.      *   
  60.      * @param record  
  61.      * @return  
  62.      */  
  63.     @InsertProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  64.     int insertSelective(T record);  
  65.   
  66.     /**  
  67.      * 根据实体类中字段不为null的条件进行删除,条件全部使用=号and条件  
  68.      *   
  69.      * @param key  
  70.      * @return  
  71.      */  
  72.     @DeleteProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  73.     int delete(T key);  
  74.   
  75.     /**  
  76.      * 通过主键进行删除,这里最多只会删除一条数据 单个字段做主键时,可以直接写主键的值 联合主键时,key可以是实体类,也可以是Map  
  77.      *   
  78.      * @param key  
  79.      * @return  
  80.      */  
  81.     @DeleteProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  82.     int deleteByPrimaryKey(Object key);  
  83.   
  84.     /**  
  85.      * 根据主键进行更新,这里最多只会更新一条数据 参数为实体类  
  86.      *   
  87.      * @param record  
  88.      * @return  
  89.      */  
  90.     @UpdateProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  91.     int updateByPrimaryKey(T record);  
  92.   
  93.     /**  
  94.      * 根据主键进行更新 只会更新不是null的数据  
  95.      *   
  96.      * @param record  
  97.      * @return  
  98.      */  
  99.     @UpdateProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  100.     int updateByPrimaryKeySelective(T record);  
  101.       
  102.     /**  
  103.      * 根据主键ID批量删除  
  104.      *   
  105.      * @param key  
  106.      * @return  
  107.      */  
  108.     @DeleteProvider(type = LiuzwMapperProvider.class, method = "dynamicSQL")  
  109.     int deleteByIDS(@Param("ids") Object[] key);  
  110.   
  111. }  
接口写好以后就需要实现类,大部分是沿用默认的方法,从写了三个查询方法,添加了一个批量删除方法(上面接口中新增了一个批量删除的接口)

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.github.liuzw.mapper.Mapper;  
  2.   
  3. import static org.apache.ibatis.jdbc.SqlBuilder.BEGIN;  
  4. import static org.apache.ibatis.jdbc.SqlBuilder.DELETE_FROM;  
  5. import static org.apache.ibatis.jdbc.SqlBuilder.SQL;  
  6.   
  7. import java.util.ArrayList;  
  8. import java.util.List;  
  9. import java.util.Set;  
  10.   
  11. import org.apache.ibatis.builder.StaticSqlSource;  
  12. import org.apache.ibatis.jdbc.SQL;  
  13. import org.apache.ibatis.mapping.MappedStatement;  
  14. import org.apache.ibatis.mapping.ParameterMapping;  
  15. import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;  
  16. import org.apache.ibatis.scripting.xmltags.IfSqlNode;  
  17. import org.apache.ibatis.scripting.xmltags.MixedSqlNode;  
  18. import org.apache.ibatis.scripting.xmltags.SqlNode;  
  19. import org.apache.ibatis.scripting.xmltags.StaticTextSqlNode;  
  20. import org.apache.ibatis.scripting.xmltags.TextSqlNode;  
  21. import org.apache.ibatis.scripting.xmltags.WhereSqlNode;  
  22.   
  23. import com.github.abel533.mapper.MapperProvider;  
  24. import com.github.abel533.mapperhelper.EntityHelper;  
  25. import com.github.abel533.mapperhelper.MapperHelper;  
  26. import com.github.liuzw.threadlocal.DynamicColumnThreadLocal;  
  27. /**  
  28.  * 改写默认的MapperProvider,添加根据id批量删除方法,支持动态字段的查询  
  29.  *   
  30.  * @author good-zhiwei 刘志伟  
  31.  * @data 2016年7月10日 下午12:41:40  
  32.  */  
  33. public class LiuzwMapperProvider extends MapperProvider {  
  34.   
  35.     public LiuzwMapperProvider(Class<?> mapperClass, MapperHelper mapperHelper) {  
  36.         super(mapperClass, mapperHelper);  
  37.         // TODO Auto-generated constructor stub  
  38.     }  
  39.   
  40.     /**  
  41.      * 根据本地线程获取需要查询的字段  
  42.      * @param entityClass  
  43.      * @return  
  44.      */  
  45.     private String getColumn(Class<?> entityClass){  
  46.         String tableColumn;  
  47.         if(DynamicColumnThreadLocal.get()!=null){  
  48.             tableColumn=DynamicColumnThreadLocal.get();  
  49.         }  
  50.         else{  
  51.             tableColumnEntityHelper.getSelectColumns(entityClass);  
  52.         }  
  53.         return tableColumn;  
  54.     }  
  55.     /**  
  56.      * 查询  
  57.      *  
  58.      * @param ms  
  59.      * @return  
  60.      */  
  61.     @Override  
  62.     public SqlNode select(MappedStatement ms) {  
  63.         Class<?> entityClass = getSelectReturnType(ms);  
  64.         //修改返回值类型为实体类型  
  65.         setResultType(ms, entityClass);  
  66.         List<SqlNode> sqlNodes = new ArrayList<SqlNode>();  
  67.         //静态的sql部分:select column ... from table  
  68.         sqlNodes.add(new StaticTextSqlNode("SELECT "  
  69.                 + getColumn(entityClass)  
  70.                 + " FROM "  
  71.                 + tableName(entityClass)));  
  72.         //将if添加到<where>  
  73.         sqlNodes.add(new WhereSqlNode(ms.getConfiguration(), getAllIfColumnNode(entityClass)));  
  74.         StringBuilder orderBy = new StringBuilder();  
  75.         for (EntityHelper.EntityColumn column : EntityHelper.getColumns(entityClass)) {  
  76.             if (column.getOrderBy() != null) {  
  77.                 orderBy.append(column.getColumn()).append(" ").append(column.getOrderBy()).append(",");  
  78.             }  
  79.         }  
  80.         if (orderBy.length() > 0) {  
  81.             orderBy.insert(0, "order by");  
  82.             sqlNodes.add(new StaticTextSqlNode(orderBy.substring(0, orderBy.length() - 1)));  
  83.         }  
  84.         return new MixedSqlNode(sqlNodes);  
  85.     }  
  86.   
  87.     /**  
  88.      * 根据主键进行查询  
  89.      *  
  90.      * @param ms  
  91.      */  
  92.     @Override  
  93.     public void selectByPrimaryKey(MappedStatement ms) {  
  94.         final Class<?> entityClass = getSelectReturnType(ms);  
  95.         //获取主键字段映射  
  96.         List<ParameterMapping> parameterMappings = getPrimaryKeyParameterMappings(ms);  
  97.         //开始拼sql  
  98.         String sql = new SQL() {{  
  99.             //select全部列  
  100.             SELECT(getColumn(entityClass));  
  101.             //from表  
  102.             FROM(tableName(entityClass));  
  103.             //where条件,主键字段=#{property}  
  104.             WHERE(EntityHelper.getPrimaryKeyWhere(entityClass));  
  105.         }}.toString();  
  106.         //使用静态SqlSource  
  107.         StaticSqlSource sqlSource = new StaticSqlSource(ms.getConfiguration(), sql, parameterMappings);  
  108.         //替换原有的SqlSource  
  109.         setSqlSource(ms, sqlSource);  
  110.         //将返回值修改为实体类型  
  111.         setResultType(ms, entityClass);  
  112.     }  
  113.   
  114.     /**  
  115.      * 根据Example查询  
  116.      *  
  117.      * @param ms  
  118.      * @return  
  119.      */  
  120.     @Override  
  121.     public SqlNode selectByExample(MappedStatement ms) {  
  122.         Class<?> entityClass = getSelectReturnType(ms);  
  123.         //将返回值修改为实体类型  
  124.         setResultType(ms, entityClass);  
  125.         List<SqlNode> sqlNodes = new ArrayList<SqlNode>();  
  126.         //静态的sql部分:select column ... from table  
  127.         sqlNodes.add(new StaticTextSqlNode("SELECT"));  
  128.         IfSqlNode distinctSqlNode = new IfSqlNode(new StaticTextSqlNode("DISTINCT"), "distinct");  
  129.         sqlNodes.add(distinctSqlNode);  
  130.         sqlNodes.add(new StaticTextSqlNode(EntityHelper.getSelectColumns(entityClass) + " FROM " + tableName(entityClass)));  
  131.         IfSqlNode ifNullSqlNode = new IfSqlNode(exampleWhereClause(ms.getConfiguration()), "_parameter != null");  
  132.         sqlNodes.add(ifNullSqlNode);  
  133.         IfSqlNode orderByClauseSqlNode = new IfSqlNode(new TextSqlNode("order by ${orderByClause}"), "orderByClause != null");  
  134.         sqlNodes.add(orderByClauseSqlNode);  
  135.         return new MixedSqlNode(sqlNodes);  
  136.     }  
  137.     /**  
  138.      * 通过ids批量删除  
  139.      *   
  140.      * @param ms  
  141.      * @return  
  142.      */  
  143.     public SqlNode deleteByIDS(MappedStatement ms) {  
  144.         Class<?> entityClass = getSelectReturnType(ms);  
  145.         Set<EntityHelper.EntityColumn> entityColumns = EntityHelper.getPKColumns(entityClass);  
  146.         EntityHelper.EntityColumn entityColumn = null;  
  147.         for (EntityHelper.EntityColumn entity : entityColumns) {  
  148.             entityColumn = entity;  
  149.             break;  
  150.         }  
  151.         EntityHelper.EntityColumn column = entityColumn;  
  152.         List<SqlNode> sqlNodes = new ArrayList<SqlNode>();  
  153.         // 开始拼sql  
  154.         BEGIN();  
  155.         // delete from table  
  156.         DELETE_FROM(tableName(entityClass));  
  157.         // 得到sql  
  158.         String sql = SQL();  
  159.         // 静态SQL部分  
  160.         sqlNodes.add(new StaticTextSqlNode(sql + " WHERE " + column.getColumn() + " IN "));  
  161.         // 构造foreach sql  
  162.         SqlNode foreach = new ForEachSqlNode(ms.getConfiguration(), new StaticTextSqlNode("#{"  
  163.                 + column.getProperty() + "}"), "ids", "index", column.getProperty(), "(", ")", ",");  
  164.         sqlNodes.add(foreach);  
  165.         return new MixedSqlNode(sqlNodes);  
  166.     }  
  167. }  
这样基于原来的两个拦截器就改造完毕。

还需要一个本地线程类用于保存需要查询的字段

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.github.liuzw.threadlocal;  
  2. /**  
  3.  * 需要查询的字段放到本地线程  
  4.  *   
  5.  * @author good-zhiwei 刘志伟  
  6.  * @data 2016年7月10日 下午12:52:52  
  7.  */  
  8. public class DynamicColumnThreadLocal {  
  9.     public static final  ThreadLocal<String> THREAD_DYNAMICCOLUMN=new ThreadLocal<String>();  
  10.       
  11.     /**  
  12.      * 把当前需要执行的sql的列绑定到线程  
  13.      * @param user  
  14.      */  
  15.     public static void set(String dynamicColumn){  
  16.         THREAD_DYNAMICCOLUMN.set(dynamicColumn);  
  17.     }  
  18.     /**  
  19.      * 获取当前需要执行sql的列  
  20.      * @param user  
  21.      */  
  22.     public static String get(){  
  23.        return THREAD_DYNAMICCOLUMN.get();  
  24.     }  
  25.     /**  
  26.      * 清除当前线程  
  27.      */  
  28.     public static void clear(){  
  29.         THREAD_DYNAMICCOLUMN.set(null);  
  30.     }  
  31. }  
ok,到这来就算大功告成,整个拦截器就完全改造完成

接下来就需要告诉大家怎么样用了

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE configuration  
  3.   PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  4.   "http://mybatis.org/dtd/mybatis-3-config.dtd">  
  5. <!-- mybatis配置 -->  
  6. <configuration>  
  7.     <settings>  
  8.         <!-- 开启驼峰映射 -->  
  9.         <setting name="mapUnderscoreToCamelCase" value="true" />  
  10.     </settings>  
  11.     <!-- 配置插件 -->  
  12.     <plugins>  
  13.         <!-- 分页查询控件 -->  
  14.         <plugin interceptor="com.github.pagehelper.PageHelper">  
  15.             <property name="dialect" value="mysql" />  
  16.             <!-- 该参数默认为false -->  
  17.             <!-- 设置为true时,使用RowBounds分页会进行count查询 -->  
  18.             <property name="rowBoundsWithCount" value="true" />  
  19.         </plugin>  
  20.         <!-- 通用mapper -->  
  21.         <plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">  
  22.             <property name="identity" value="mysql" />  
  23.             <property name="mappers" value="com.github.liuzw.mapper.Mapper.LiuzwMapper" />  
  24.         </plugin>  
  25.     </plugins>  
  26. </configuration>  
通过mapper ,里面的plugin 的interceptor属性就是配置通用Mapper拦截器 identity属性就针对数据库类型,这来是mysql,mappers属性就是我们真正的执行方法的接口(里面有具体的增、删、改、查的方法) 然后自己的mapper只需要继承这来配置的接口(LiuzwMapper)就具备了上个接口的所以方法,所以实现了零代码编写,零配置。因为接口是泛型所以在继承的时候需要给一个po对象(po对象的属性必须和数据库的字段保持一致,必须加上对应的注解,这个就是缺点,这样一来我们的po对象就只能是一个单纯的po对象,而不能有过多的业务参与,现在的程序设计中也推荐用这种方法,po对象和vo对象分离)

具体用法如下:

po对象

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.liuzw.study.pojo;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import javax.persistence.Column;  
  6. import javax.persistence.GeneratedValue;  
  7. import javax.persistence.GenerationType;  
  8. import javax.persistence.Id;  
  9. import javax.persistence.Table;  
  10.   
  11. import org.springframework.format.annotation.DateTimeFormat;  
  12.   
  13. @Table(name="tb_user")//指定表名称  
  14. public class User implements java.io.Serializable{  
  15.       
  16.     private static final long serialVersionUID = 1L;  
  17.   
  18.     @Id  
  19.     @GeneratedValue(strategy=GenerationType.IDENTITY)  
  20.     private Long id;  
  21.   
  22.     // 用户名  
  23.     @Column(name="user_name")  
  24.     private String userName;  
  25.   
  26.     // 密码  
  27.     private String password;  
  28.   
  29.     // 姓名  
  30.     private String name;  
  31.   
  32.     // 年龄  
  33.     private Integer age;  
  34.   
  35.     // 性别,1男性,2女性  
  36.     private Integer sex;  
  37.   
  38.     // 出生日期  
  39.     @DateTimeFormat(pattern="yyyy-MM-dd")  
  40.     private Date birthday;  
  41.   
  42.     // 创建时间  
  43.     private Date created;  
  44.   
  45.     // 更新时间  
  46.     private Date updated;  
  47.   
  48.     public Long getId() {  
  49.         return id;  
  50.     }  
  51.   
  52.     public void setId(Long id) {  
  53.         this.id = id;  
  54.     }  
  55.   
  56.     public String getUserName() {  
  57.         return userName;  
  58.     }  
  59.   
  60.     public void setUserName(String userName) {  
  61.         this.userName = userName;  
  62.     }  
  63.   
  64.     public String getPassword() {  
  65.         return password;  
  66.     }  
  67.   
  68.     public void setPassword(String password) {  
  69.         this.password = password;  
  70.     }  
  71.   
  72.     public String getName() {  
  73.         return name;  
  74.     }  
  75.   
  76.     public void setName(String name) {  
  77.         this.name = name;  
  78.     }  
  79.   
  80.     public Integer getAge() {  
  81.         return age;  
  82.     }  
  83.   
  84.     public void setAge(Integer age) {  
  85.         this.age = age;  
  86.     }  
  87.   
  88.     public Integer getSex() {  
  89.         return sex;  
  90.     }  
  91.   
  92.     public void setSex(Integer sex) {  
  93.         this.sex = sex;  
  94.     }  
  95.   
  96.     public Date getBirthday() {  
  97.         return birthday;  
  98.     }  
  99.   
  100.     public void setBirthday(Date birthday) {  
  101.         this.birthday = birthday;  
  102.     }  
  103.   
  104.     public Date getCreated() {  
  105.         return created;  
  106.     }  
  107.   
  108.     public void setCreated(Date created) {  
  109.         this.created = created;  
  110.     }  
  111.   
  112.     public Date getUpdated() {  
  113.         return updated;  
  114.     }  
  115.   
  116.     public void setUpdated(Date updated) {  
  117.         this.updated = updated;  
  118.     }  
  119.   
  120.     @Override  
  121.     public String toString() {  
  122.         return "User [id=" + id + "userName=" + userName + "password=" + password + "name=" + name  
  123.                 + ", age=" + age + "sex=" + sex + "birthday=" + birthday + "created=" + created  
  124.                 + ", updated=" + updated + "]";  
  125.     }  
  126.   
  127. }  
自定义mapper:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.liuzw.study.mapper;  
  2.   
  3. import com.github.liuzw.mapper.Mapper.LiuzwMapper;  
  4. import com.liuzw.study.pojo.User;  
  5. public interface UserMapper extends LiuzwMapper<User> {  
  6.   
  7. }  
可以看见基本就是零代码编写,只需要继承我们的通用mapper

service 代码:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.liuzw.study.service;  
  2.   
  3. import java.util.List;  
  4.   
  5. import org.springframework.beans.factory.annotation.Autowired;  
  6. import org.springframework.stereotype.Service;  
  7.   
  8. import com.github.liuzw.threadlocal.DynamicColumnThreadLocal;  
  9. import com.liuzw.study.mapper.UserMapper;  
  10. import com.liuzw.study.pojo.User;  
  11.   
  12. @Service  
  13. public class UserService {  
  14.     @Autowired  
  15.    private UserMapper userMapper;  
  16.     public List<User> queryAll(){  
  17.         User record=new User();  
  18.         record.setUserName("liuzhiwei");  
  19.         //SEX,AGE,UPDATED,USER_NAME USERNAME,NAME,BIRTHDAY,CREATED,ID,PASSWORD  
  20.         DynamicColumnThreadLocal.set("id,sex,age");//动态查询  
  21.         return userMapper.select(record);  
  22.         //return userMapper.select(record);  
  23.     }  
  24. }  
主要:其他的配置包括spring整合mybatis 这些还是必不可少的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值