mybatis泛型(一)
mybatis的确很方便,可以随意配置sql语句,并根据参数生成指定的sql,也可以根据查询结果生成指定对象
但是有一点非常恐怖,就是每个数据库表都必须有一个配置,等于在一个系统里做了很多重复的工作,
因为几乎每个表都用到getById,update等操作,今天我们就尝试着把这些操作提取出来,一次性完成.
而不要重复重复再重复. 设想一下一个大型项目,几百张表,要生成几百个mapper.xml 而且这些xml内容差不多.
先来看看准备工作
定义entity的基类,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
id字段很重要,其余看公司规定.
这里Id使用的是泛型,也就是说它可以使 int,long,甚至String
真正的entity对象定义起来就很容易啦
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
然后是定义mapper接口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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(); }
这里基本包含我们常用的查询,其余需要级联查询,多表关联的继承这个接口,然后定义这个不常用查询的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也不尽相同,所以需要有不同的调用方式
注册过程如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
过程并不复杂