yangyang12138

导航

mybatis泛型(一)

mybatis的确很方便,可以随意配置sql语句,并根据参数生成指定的sql,也可以根据查询结果生成指定对象

但是有一点非常恐怖,就是每个数据库表都必须有一个配置,等于在一个系统里做了很多重复的工作,

因为几乎每个表都用到getById,update等操作,今天我们就尝试着把这些操作提取出来,一次性完成.

而不要重复重复再重复. 设想一下一个大型项目,几百张表,要生成几百个mapper.xml  而且这些xml内容差不多.

先来看看准备工作

定义entity的基类,

import java.util.Date;

public class BaseEntity<ID> {
    private ID id;
    private ID creatorId;
    private ID updaterId;
    private Date createDate;
    private Date updateDate;
    private Long updateCount;
    private Integer isDelete;

    public ID getId() {
        return id;
    }

    public void setId(ID id) {
        this.id = id;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public ID getCreatorId() {
        return creatorId;
    }

    public void setCreatorId(ID creatorId) {
        this.creatorId = creatorId;
    }

    public ID getUpdaterId() {
        return updaterId;
    }

    public void setUpdaterId(ID updaterId) {
        this.updaterId = updaterId;
    }

    public Long getUpdateCount() {
        return updateCount;
    }

    public void setUpdateCount(Long updateCount) {
        this.updateCount = updateCount;
    }

    public Integer getIsDelete() {
        return isDelete;
    }

    public void setIsDelete(Integer isDelete) {
        this.isDelete = isDelete;
    }
}
View Code

id字段很重要,其余看公司规定.

这里Id使用的是泛型,也就是说它可以使 int,long,甚至String

真正的entity对象定义起来就很容易啦

public class SysUser extends BaseEntity<Integer> {
    private Integer userAge;
    private String userName;
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public Integer getUserAge() {
        return userAge;
    }
    public void setUserAge(Integer userAge) {
        this.userAge = userAge;
    }
}
View Code

然后是定义mapper接口

public interface BaseMapper<T extends BaseEntity<ID>,ID> {
    T getById(ID id);
    int insert(T entity);
    int update(T entity);
    
    int deleteByIds(@Param("ids")ID... ids);
    int deleteBy(Criteria criteria);
    
    int removeByIds(@Param("ids")ID... ids);
    int removeBy(Criteria criteria);
    
    List<T> getBy(Criteria criteria);
    
    int countAll();
    int countBy(Criteria criteria);
    
    int insertAll(List<T> list);
    int updateBy(@Param("criteria")Criteria criteria,@Param("params")T entity);
    
    List<T> findAll();
}
View Code

这里基本包含我们常用的查询,其余需要级联查询,多表关联的继承这个接口,然后定义这个不常用查询的mapper.xml

public interface SysUserMapper extends BaseMapper<SysUser,Integer>{
        int maxId();
    }

这个只是Demo自然没什么复杂的东西可以定义

了解过mybatis的人应该知道,mapper接口是需要注册才能使用的,

要写使用这些公用的方法还需要一些步骤.

我们可以使用代码生成器,生成如下类:

public class MapperContainer {
    public static interface SysUserMapper extends BaseMapper<SysUser,Integer>{
        
    }
    
    public static interface UserlogMapper extends BaseMapper<Userlog,Integer>{
        
    }
}

再有复杂定义时可以继承MapperContainer.XXX,  MapperContainer 有两个作用,第一个作用是防止某些表根本没有复杂定义,使用基本查询即可. 这样不用手写一个接口

第二就是为了方便注册

我们先来看看使用的Demo

public class Demo {

    public static void main(String[] args) throws IOException {
        Reader reader = Resources.getResourceAsReader("mybaits-config.xml");
        Configuration config = new XMLConfigBuilder(reader, null, null).parse();
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
        
        OrmProvider.getMapperRegister().registerContainer(MapperContainer.class, config);
        
        SqlSession session = factory.openSession(false);
        
        MapperContainer.UserlogMapper mapper = session.getMapper(MapperContainer.UserlogMapper.class);
        
        List<Userlog> list = new ArrayList<Userlog>();
        for(int i=0;i<10;i++){
            list.add(new Userlog());
            list.get(i).setContext("这是测试内容"+i);
            list.get(i).setIpAddress("192.168.56." + i);
        }
        mapper.insertAll(list);
        
        session.commit();
        session.close();
    }

}
OrmProvider.getMapperRegister().registerContainer(MapperContainer.class, config);就是在对这些mapper做注册操作
我们先来看看这个注册接口
public interface MapperRegister {
    InputStream createMapper(Class<?> cls);
    void register(Class<?> cls,Configuration config);
    void registerContainer(Class<?> container,Configuration config);
}

从这个接口上就能看出方便性来,因为如果不是使用容器类,就必须对这些接口一个个注册.

当然这只是一个接口,因为不同数据库的sql也不尽相同,所以需要有不同的调用方式

注册过程如下

public class MysqlMapperRegister extends AbstractMapperRegister {

    public final static String XML_HEADER = "<?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\">";
    
    private static HashSet<Integer> set = new HashSet<Integer>();
    
    @SuppressWarnings("rawtypes")
    public InputStream createMapper(Class<?> cls) {
        if(!BaseMapper.class.isAssignableFrom(cls)){
            throw new RuntimeException(cls.getName() + "不是BaseMapper的子类,无法完成注册");
        }
        
        Class entityCls = (Class) ((ParameterizedType) cls.getGenericInterfaces()[0]).getActualTypeArguments()[0];
        Class idCls = (Class) ((ParameterizedType) cls.getGenericInterfaces()[0]).getActualTypeArguments()[1];
        
        StringBuilder builder = new StringBuilder(XML_HEADER);
        builder.append("<mapper namespace='");
        builder.append(cls.getName());
        builder.append("'>");
        
        builder.append("<resultMap type='"+entityCls.getName()+"' id='"+entityCls.getName().replaceAll("[.]", "")+"_resultMap'>");
        for(Field field:getAllFields(entityCls)){
            builder.append("<id property='"+field.getName()+"' column='"+OrmProvider.getCurrentOrmStrategy().filed2Column(field)+"'  />");
        }
        builder.append("</resultMap>");
        
        builder.append("<select id='getById' parameterType='"+idCls.getName()+"' resultType='");
        builder.append(entityCls.getName());
        builder.append("'>select * from ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        builder.append(" where id = #{id} and ");
        builder.append(OrmProvider.getCurrentODeleteStrategy().getUnDeleteCnd());
        builder.append("</select>");
        
        builder.append("<insert id='insert' parameterType='");
        builder.append(entityCls.getName());
        builder.append("' useGeneratedKeys='true' keyProperty='id' >insert into ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        
        builder.append(" <trim prefix='(' suffix=')' suffixOverrides=',' >");
        for(Field field:getAllFields(entityCls)){
            builder.append("<if test='"+field.getName()+" != null' >"+OrmProvider.getCurrentOrmStrategy().filed2Column(field)+",</if>");
        }
        builder.append("</trim>");
        
        builder.append("<trim prefix='values (' suffix=')' suffixOverrides=',' >");
        for(Field field:getAllFields(entityCls)){
            builder.append("<if test='"+field.getName()+" != null' >#{"+field.getName()+"},</if>");
        }
        builder.append("</trim>");
        
        builder.append("</insert>");
        
        builder.append("<update id='deleteByIds' parameterType='java.util.List'>");
        builder.append(OrmProvider.getCurrentODeleteStrategy().createDeleteSql(entityCls));
        builder.append(" where id in ");
        builder.append("<foreach item='item' index='index' collection='ids' open='(' separator=',' close=')'>#{item}</foreach>");
        builder.append(" </update>");
        
        builder.append("<update id='deleteBy' parameterType='com.qdyq.common.criteria.Criteria'>");
        builder.append(OrmProvider.getCurrentODeleteStrategy().createDeleteSql(entityCls));
        builder.append("<if test='_parameter != null' ><where ><include refid='Global.Where_Clause' /></where></if>");
        builder.append(" </update>");
        
        builder.append("<update id='update' parameterType='");
        builder.append(entityCls.getName());
        builder.append("'>update ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        builder.append(" <set>");
        for(Field field:getAllFields(entityCls)){
            builder.append("<if test='"+field.getName()+" != null' >"+OrmProvider.getCurrentOrmStrategy().filed2Column(field)+" = #{"+field.getName()+"},</if>");
        }
        builder.append("</set> ,update_count=update_count+1  where id = #{id} and ");
        builder.append(OrmProvider.getCurrentODeleteStrategy().getUnDeleteCnd());
        builder.append("</update>");
        
        builder.append("<delete id='removeByIds' parameterType='java.util.List'>");
        builder.append("delete from ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        builder.append(" where id in ");
        builder.append("<foreach item='item' index='index' collection='ids' open='(' separator=',' close=')'>#{item}</foreach>");
        builder.append(" </delete>");
        
        builder.append("<delete id='removeBy' parameterType='com.qdyq.common.criteria.Criteria'>");
        builder.append("delete from ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        builder.append(" <if test='_parameter != null' ><where ><include refid='Global.Where_Clause' /></where></if>");
        builder.append(" </delete>");
        
        builder.append("<select id='findAll' resultMap='");
        builder.append(entityCls.getName().replaceAll("[.]", "")+"_resultMap");
        builder.append("'>select * from ");
        builder.append(entityCls.getSimpleName());
        builder.append(" where ");
        builder.append(OrmProvider.getCurrentODeleteStrategy().getUnDeleteCnd());
        builder.append("</select>");
        
        builder.append("<insert id='insertAll' parameterType='java.util.List'");
        builder.append(" useGeneratedKeys='true' keyProperty='id' >insert into ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        
        builder.append(" (");
        boolean notFirst = false;
        StringBuilder valuesBuilder = new StringBuilder("(");
        for(Field field:getAllFields(entityCls)){
            if("id".equals(field.getName()))continue;
            if(notFirst){
                builder.append(',');
                valuesBuilder.append(',');
            }else
                notFirst = true;
            
            valuesBuilder.append("#{item." + field.getName() +"}");
            builder.append(OrmProvider.getCurrentOrmStrategy().filed2Column(field));
        }
        builder.append(") values ");
        valuesBuilder.append(")");
        builder.append("<foreach collection='list' item='item' index='index' separator=',' > ");
        builder.append(valuesBuilder.toString());
        builder.append("</foreach>");
        builder.append("</insert>");

        builder.append("<select id='getBy' parameterType='com.qdyq.common.criteria.Criteria' resultMap='"+entityCls.getName().replaceAll("[.]", "")+"_resultMap' >");
        builder.append("select * from ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        builder.append("<if test='_parameter != null' ><where ><include refid='Global.Where_Clause' /></where></if>");
        builder.append("<if test='hasOrderBy' >order by<foreach collection='orderBys' item='orderBy' separator=',' >${orderBy}</foreach></if>");
        builder.append("</select>");
        
        builder.append("<select id='countBy' parameterType='com.qdyq.common.criteria.Criteria' resultType='int'>");
        builder.append("select count(*) from ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        builder.append("<if test='_parameter != null' ><where ><include refid='Global.Where_Clause' /></where></if>");
        builder.append("</select>");
        
        builder.append("<select id='countAll' resultType='int'>");
        builder.append("select count(*) from ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        builder.append(" where ");
        builder.append(OrmProvider.getCurrentODeleteStrategy().getUnDeleteCnd());
        builder.append("</select>");
        
        builder.append("<update id='updateBy'  parameterType='com.qdyq.common.criteria.Criteria'>");
        builder.append("update ");
        builder.append(OrmProvider.getCurrentOrmStrategy().class2Table(entityCls));
        builder.append(" <set>");
        for(Field field:getAllFields(entityCls)){
            if("id".equals(field.getName()))continue;
            builder.append("<if test='params."+field.getName()+" != null' >"+OrmProvider.getCurrentOrmStrategy().filed2Column(field)+" = #{params."+field.getName()+"},</if>");
        }
        builder.append("</set> ,update_count=update_count+1 ");
        builder.append("<if test='criteria != null' ><where><include refid='Global.Where_Clause2'/></where></if>");
        builder.append("</update>");
        
        builder.append("</mapper>");
        
        System.out.println(builder.toString());
        
        return new ByteArrayInputStream(builder.toString().getBytes());
    }

    public void register(Class<?> cls, Configuration config) {
        
        if(!set.contains(config.hashCode())){
            synchronized(this){
                if(!set.contains(config.hashCode()))
                    try {
                        set.add(config.hashCode());
                        new XMLMapperBuilder(Resources.getResourceAsStream("GlobalMapper.xml"),config,"xmlCoder_Global",config.getSqlFragments()).parse();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }
        }
        
        
        new XMLMapperBuilder(createMapper(cls),config,"xmlCoder_" + cls.getName().replaceAll("[.]", ""),config.getSqlFragments()).parse();
    }
    
    private List<Field> getAllFields(Class<?> cls){
        if(!BaseEntity.class.isAssignableFrom(cls)){
            throw new RuntimeException("entity的基類必須是BaseEntity");
        }
        Field[] fs = cls.getDeclaredFields();
        List<Field> result = new ArrayList<Field>();
        for(Field f:fs){
            if(f.getAnnotation(MapForbidden.class)==null){
                result.add(f);
            }
        }
        if(!cls.equals(BaseEntity.class))result.addAll(getAllFields(cls.getSuperclass()));
        return result;
    }
    
}
View Code

过程并不复杂

posted on 2017-09-20 19:38  杨杨09265  阅读(6036)  评论(0编辑  收藏  举报