Mybatis 通用Crud

Stella981
• 阅读 1010

更新日志

1 新增批量操作数据方法:批量插入,根据条件删除,根据条件更新指定的列名-字段值。

2 新增高级查询方法:可设置查询列,查询条件,排序,分页。

3 根据1、2更新接口。

4 更改dao接口方法实现方式,统一采用GeneralMapper.xml编写sql,弃用GeneralDaoProvider。

一 关于Mybatis

1.1 mybatis 的优点

1 轻量级ORM 。

2 提供了完善的缓存机制。

3 mapper.xml 原声SQL更清晰灵活,且sql便于SQL调优。

4 resultType resultMap 处理返回结果集,与pojo解耦。

1.2 mybatis的使用体验

这里只将Mybatis不便于使用之处做以说明。

1 需要为每张表写一个dao接口和mapper.xml,这对于开发者来讲就不是很友好了,假设系统有30张业务表,呵呵。

2 虽然有generator 工具,可以自动生成dao 接口和mapper.xml,是可以不用自己去写这些代码了,但是还是有2个缺点:

(1) generator只是提供了基础的增删改查功能,复杂的sql还是要自己去添加。

(2) 还是上面的问题,每张表都要有dao接口和mapper.xml。

1.3 关于物理分页

对于分页的功能,仁者见仁,智者见智吧,就我个人的观点来讲,我觉得没必要。

上面也描述了mybatis的优点之一就是mapper.xml中的原声SQL,特别是对于复杂的SQL语句,原声SQL很有魅力。

如果你分页的sql封装成如下的格式,

<!-- mysql page封装 -->
select count(1) as 'totalNum',t.*  from (select  user_id , user_name from user ) t limit startRowNo,rowNum

<!-- mysql 原生分页 -->
select count(1) as 'totalNum', user_id , user_name from user limit startRowNo,rowNum

我觉得,还是算了吧,不如原声SQL来的实在。select * 我就不讲了,select * from (sql) 根本就没必要嘛。

如果说你希望实现一个灵活的查询方法,有分页参数就按分页查询,没有分页参数就查询全部。那么我的观点是,mybatis 提供了标签,代码如下:

   <!-- mapper.xml -->
   <select id="selectByPage" parameterType="map" resultType="hashmap">
    
    select count(1) as 'totalNum',user_id,user_name
    from user
    
    <if test="pageParam != null" >
    limit #{pageParam.startRowNo},#{pageParam.pageSize}
    </if>
    
    </select>

如果说你希望分页方法能够更灵活,希望实现一个方法对任何一张表都可以分页,代码可以这样写

   <select id="selectByPage" parameterType="map" resultType="hashmap">
    
    select ${queryColumn}
    from ${tableName}
    
    <if test="page != null" >
    limit #{page.startRowNo},#{page.pageSize}
    </if>
    
    </select>

关于分页,就这么多,仅个人观点,欢迎批评、建议或讨论。

二 Mybatis通用Crud设计

2.1 需求

基于1.2,希望提供类似于hibernate中的load、insert、delete、save方法,对于基础的数据访问层操作,系统只需要存在一处,便于统一的管理,减少不必要的代码。

基于1.1,避免使用java封装字符串sql,mapper.xml,@select,@selectProvider三选一。

2.2 设计

2.2.1 通用crud接口

希望实现的功能方法接口,因篇幅有限,这里就只给出主键查询的接口。

全部接口声明可以查看对应代码,或者,

查看上一篇博文Mybatis 通用Crud-接口预览,https://my.oschina.net/LittleNewbie/blog/785947

/**
 * 通用Crud 数据访问层接口
 * 
 * @author svili
 * @date 2016年11月11日
 *
 */
public interface CrudServiceInter {

    /**
     * 根据主键查询
     * @param <T> pojo类
     * @param clazz pojo类-class对象
     * @param primaryValue  主键值
     * @return pojo对象
     */
    <T> T selectByPrimaryKey(Class<T> clazz, Object primaryValue) throws Exception;
}

2.2.2 根据主键查询

1 首先,明确接口

<T> T selectByPrimaryKey(Class<T> clazz, Object primaryValue) throws Exception;

<2017-02-18更新>

主键类型不做限制。

2 分析下mapper.xml中的sql

<select id="selectByPrimaryKey" parameterType="map" resultType="hashmap">
    select 
    <foreach item="columnName" index="index" collection="queryColumn" separator="," >
        ${columnName}
    </foreach>
    from ${tableName}
    where ${primaryKey} = #{primaryValue}
</select>

注意:不要使用<![CDATA[  ]]>去尝试对这段sql进行转义,会导致标签不被解析处理。

关于xml中的<![CDATA[  ]]> ,自行百度get噢。

3 明确sql中的参数

sql中需要传递的参数有4个,分别是:tableName(表名),queryColumn(查询字段集),primaryKey(主键列名),primaryValue(主键值)。

4  接口参数与sql参数转化

(1)tableName

从Java类到数据库的表名,我们可以获取两个信息,一个是类名,第二个是JPA的@Table注解。

要注意两点,1.注解优先,2.Java的命名规则为驼峰,数据库的命名规则为下划线。如果你不按套路出牌,还是要好好想想如何去对应转换。

//获取pojo表名
public static String getTableName(Class<?> clazz) {
        // 驼峰转下划线
        String tableName = StringUtil.camelToUnderline(clazz.getName());
        // 判断是否有Table注解
        if (clazz.isAnnotationPresent(Table.class)) {
            // 获取注解对象
            Table table = clazz.getAnnotation(Table.class);
            // 设置了name属性
            if (!table.name().trim().equals("")) {
                return table.name();
            }
        }
        return tableName;
}

(2)queryColumn

直接获取字段名就好,注意驼峰转下划线。

//获取所有字段名
public static List<String> getAllColumns(Class<?> clazz) {
        List<String> columns = new ArrayList<String>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            columns.add(StringUtil.camelToUnderline(field.getName()));
        }
        return columns;
}

(3)primaryKey

这只能靠JPA的@Id了。

当然你可自己定制一套标准,比如每张表都固定一个主键字段名为table_id。但是我觉得完全没必要,既然有JPA的标准,为什么要去自己费脑经呢。如果说你使用oracle的guid,没脾气,呵呵。

//获取主键字段
public static Field getPrimaryFieldNotCareNull(Class<?> clazz) {
        Field field = FieldReflectUtil.findField(clazz, Id.class);
        if (field != null) {
            return field;
        } else {
            return null;
        }
}
//获取指定注解类型的字段
public static Field findField(Class<?> clazz, Class<? extends Annotation> annotationType) {
        Class<?> searchType = clazz;
        while (!Object.class.equals(searchType) && searchType != null) {
            Field[] fields = searchType.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(annotationType)) {
                    return field;
                }
            }
            searchType = searchType.getSuperclass();
        }
        return null;
}

2.2.3 其他接口

关于其他接口,其实大同小异,这里就不做说明了,只需要按照2.2.2 中的思路去查看源码就OK了。

三 源码

源码地址:https://github.com/LittleNewbie/portal

如何阅读源码:

主要4个文件 GeneralDao,GeneralMapper.xml,GeneralDaoProvider,CrudServiceImpl

GeneralDao声明了数据访问层接口;

GeneralMapper.xml 和GeneralDaoProvider(已弃用2016-11-13)配合GeneralDao。

GeneralDaoProvider已弃用 from 2016-11-13。

CrudServiceImpl 实现由接口参数转化为GeneralDao参数,并封装返回类型。

GeneralQueryParam 查询参数封装,start from 2016-11-13。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Wesley13 Wesley13
3年前
MySQL 我自己常用的语句汇总
1,更新,根据一个表更新另一个表,比如批量同步外键  方法一:  update更新表set字段(select参考数据from参考表where 参考表.id 更新表.id);  updatetable\_2m setm.column(selectcolumnfromtable\_1mpwherem
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这