java Mybatis简介

开发注意点

$与#区别

$直接传入参数,会引起注入问题
#预编译,能够避免注入问题,但是Mybatis只会当它是一个值,特别是不能在in语句中误用

也可以通过java代码进行SQL代码注入过滤

in参数的传入

通过foreach实现
Mybatis

1
2
3
4
5
6
<delete id="deleteBatch" parameterType="java.util.List">
delete from z_role_sys_menu where role_id in
<foreach item="roleId" collection="list" open="(" separator="," close=")">
#{roleId}
</foreach>
</delete>
1
int deleteBatch(List<String> roleIds);
1
2
3
4
5
6
<delete id="deleteBatch">
delete from z_role_sys_menu where role_id in
<foreach item="roleId" collection="roleIds" open="(" separator="," close=")">
#{roleId}
</foreach>
</delete>
1
int deleteBatch(Long[] roleIds);

like 写法

MySQL+Mybatis

1
select * from z_user where user_name like concat('%',#{search},'%')

Oracle+ibatis

1
2
3
4
5
6
select * from z_user 
<dynamic prepend="WHERE">
<isNotEmpty prepend="and" property="user_name">
user_name like '%' || #user_name# || '%'
</isNotEmpty>
</dynamic>

一对多关联查询

  1. 可以在service层代码进行控制,通过代码查询出一级比如部门、再循环查询部门下面的人员。最优选。
  2. 可以使用mybatis plus的写法,在xml中使用子查询,框架会进行合并成对象下面附带list的格式。需要注意left join的时候右边的表被人删除导致数据错误的情况,尽量使用右边表数据,根据情况使用join代替left join。目前来说需要定义很多xml,不好用。
    1
    2
    3
    4
    5
    6
    public PageUtils queryPage(Map<String, Object> params) {
    Page page = new Query<Map<String, Object>>(params).getPage();
    List<Map<String, Object>> list = this.baseMapper.queryDept(page, params);
    page.setRecords(list);
    return new PageUtils(page);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <!-- 详情用,分页条数会有问题 -->
    <resultMap type="vip.infotech.Dept" id="deptMap">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <collection property="user" ofType="User">
    <result property="id" column="id"></result>
    <result property="code" column="code"></result>
    </collection>
    </resultMap>
    <!-- 查询,分步,其实是执行多条语句,还不如service层业务处理,ofType可以用Map -->
    <resultMap type="vip.infotech.Dept" id="deptMapStep">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <collection property="user" column="id"
    ofType="vip.infotech.User" select="queryUserById"/>
    </resultMap>
    <!--resultMap type="java.util.Map" id="deptMapStep">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <collection property="user" column="id"
    ofType="vip.infotech.User" select="queryUserById"/>
    </resultMap-->
    <select id="queryUserById" resultType="vip.infotech.User">
    select
    b.id,
    b.code
    from dept a join user b on a.id=b.id
    where a.id = #{id}
    </select>
    <select id="queryDept" parameterType="java.util.Map" resultMap="deptMapStep">
    select
    b.id,
    b.name
    from dept a
    where a.id = #{id}
    </select>

返回ID

mybatisplus 执行insert(T var1) 后T中的id会有值

其他标签

判断参数是否存在

1
2
3
<if test="_parameter.containsKey('name')">
and name=#{name}
</if>

Mybatisplus 分页写法

自定义语句

1
2
3
4
5
6
7
8
9
public PageUtils queryPage(Map<String, Object> params) {
Page page = new Query<Map<String, Object>>(params).getPage();
List<Map<String, Object>> list = this.baseMapper.queryDept(page, params);
page.setRecords(list);
return new PageUtils(page);
}

// DeptDao.java
List<Map<String, Object>> queryDept(Page page, @param("params")Map<String, Object> params);
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--
resultType:
1、基本类型:resultType=基本类型
2、List类型:resultType=List中元素的类型
3、Map类型 单条记录:resultType =map 多条记录:resultType=Map中value的类型
-->
<select id="queryDept" parameterType="java.util.Map" resultType="java.util.Map">
select
b.id,
b.name
from dept a
where a.id = #{params.id}
</select>

ibatis spring 整合后事务控制

  1. 编程式事务:编码方式实现事务管理,TransactionTemplate,DefaultTransactionDefinition
  2. 声明式事务:可知编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现。
    1
    @Transactional(propagation=Propagation.NEVER) // java spring ibatis 提交的代码 PROPAGATION_NOT_SUPPORTED
    1
    <tx:annotation-driven transaction-manager="transactionManagerIT"/> <!--如果定义的事务管理器名称为transactionManager,那么就可以直接使用<tx:annotation-driven/>-->
    或者直接通过配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <bean id="baseTransactionProxyIT" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
    <property name="transactionManager">
    <ref bean="transactionManagerIT" />
    </property>
    <property name="transactionAttributes">
    <props>
    <prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
    <prop key="send">PROPAGATION_NOT_SUPPORTED,readOnly,-Exception</prop>
    <prop key="*">PROPAGATION_SUPPORTS,-Exception</prop>
    </props>
    </property>
    </bean>
    或者这个没试过,关闭后开启,或者获取sessionFactory操作
    1
    dao.getSqlMapClientTpl().getSqlMapClient().getCurrentConnection().setAutoCommit(false);

    Transactional注解属性说明

事务传播 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作

对于使用了PROPAGATION_REQUIRED事务的方法会等方法调用的SQL语句都执行完成才提交,可以修改成PROPAGATION_SUPPORTS这样每条SQL语句执行完成都会立即提交。
使用事务PROPAGATION_REQUIRED,数据库执行语句异常,手动抛出异常都会使事务回滚。

整合设计

返回值用HashMap还是POJO

  1. 对于非常多返回字段或者复杂关联查询可以考虑HashMap或者针对返回结果单独建立对应的VO
  2. 对于动态返回数据的sql语句,返回的结果不确定使用HashMap可能会更好一点,当然也可以再Java代码中实现对应参数的替换工作select <if type=1>mobile</if> ... from xxx
  3. 接口设计时,返回内容尽量单一,减少使用连表一对多一次查询一个集合还包含子集合的方式
  4. 没有银弹,各个方式存在即合理,具体使用要综合考量

数据库应用结合设计

  1. 可以把当前表与历史表合并,通过状态标识出最新记录(还可以标识出想要的状态,比如:上下车状态可以1标识在车上,下车后把上下车都标识为0)。
  2. 对于服务端实现复杂的功能可以放在客户端实现,比如人员下挂多个设备,通过设备标识人员状态可以把人员设备关系都给客户端,客户端自行判断,如果服务端判断复杂度非常高,需要考虑设备不同人使用,设备不同工作任务使用,不能互相冲突,各种中间关联关系。
  3. 对于批量选择新增的功能简化设计(一对多),正常逻辑(左右选项卡,右边显示已选中,左边显示待选择):集合A(前端)和B(数据库)操作,A-B为新增,B-A为删除,A∩B为不变。简化方法一:数据库先进行对应项全部删除,然后把传过来的选中项全部新增。方案二:客户端选择框只显示可以添加的项,已经添加被关联占用的项不显示,这样客户端提交后就是新增的项,后端只需进行新增操作,如果客户端需要删除就在关联详情列表进行删除操作(union多表关联选出空闲设备)
  4. 对于批量选择移动的功能简化设计(一对多),关联移动也是可以简化的,三部曲(判断新增、删除、不变)后端实现太复杂。可以前端进行两步接口调用操作:1:删除旧项、2:新增新项,还可以把这两步操作传给后端实现比如设备1从用户1转移给用户2(无中间表ID{"delType":"user","delId":1,"delDeviceId":1,"addType":"user","addId":2,"addDeviceId":1},有中间表ID{"delType":"user","delId(中间表id)":1,"addType":"car","addTargetId":2,"addDeviceId":1}

其他注意事项

  • 不要重写框架封装好的方法,有需要旧自己扩展。不然重复注入sql会有警告,mybatis plus框架注入顺序是先xml,后系统自带

参考