开发注意点
$与#区别
$
直接传入参数,会引起注入问题#
预编译,能够避免注入问题,但是Mybatis只会当它是一个值,特别是不能在in语句中误用
也可以通过java代码进行SQL代码注入过滤
in参数的传入
通过foreach实现
Mybatis
1 | <delete id="deleteBatch" parameterType="java.util.List"> |
1 | int deleteBatch(List<String> roleIds); |
1 | <delete id="deleteBatch"> |
1 | int deleteBatch(Long[] roleIds); |
like 写法
MySQL+Mybatis
1 | select * from z_user where user_name like concat('%',#{search},'%') |
Oracle+ibatis
1 | select * from z_user |
一对多关联查询
- 可以在service层代码进行控制,通过代码查询出一级比如部门、再循环查询部门下面的人员。最优选。
- 可以使用mybatis plus的写法,在xml中使用子查询,框架会进行合并成对象下面附带list的格式。需要注意left join的时候右边的表被人删除导致数据错误的情况,尽量使用右边表数据,根据情况使用join代替left join。目前来说需要定义很多xml,不好用。
1
2
3
4
5
6public 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 | <if test="_parameter.containsKey('name')"> |
Mybatisplus 分页写法
自定义语句
1 | public PageUtils queryPage(Map<String, Object> params) { |
1 | <!-- |
ibatis spring 整合后事务控制
- 编程式事务:编码方式实现事务管理,TransactionTemplate,DefaultTransactionDefinition
- 声明式事务:可知编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现。
1
// java spring ibatis 提交的代码 PROPAGATION_NOT_SUPPORTED
或者直接通过配置1
<tx:annotation-driven transaction-manager="transactionManagerIT"/> <!--如果定义的事务管理器名称为transactionManager,那么就可以直接使用<tx:annotation-driven/>-->
或者这个没试过,关闭后开启,或者获取sessionFactory操作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>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
- 对于非常多返回字段或者复杂关联查询可以考虑HashMap或者针对返回结果单独建立对应的VO
- 对于动态返回数据的sql语句,返回的结果不确定使用HashMap可能会更好一点,当然也可以再Java代码中实现对应参数的替换工作
select <if type=1>mobile</if> ... from xxx
- 接口设计时,返回内容尽量单一,减少使用连表一对多一次查询一个集合还包含子集合的方式
- 没有银弹,各个方式存在即合理,具体使用要综合考量
数据库应用结合设计
- 可以把当前表与历史表合并,通过状态标识出最新记录(还可以标识出想要的状态,比如:上下车状态可以1标识在车上,下车后把上下车都标识为0)。
- 对于服务端实现复杂的功能可以放在客户端实现,比如人员下挂多个设备,通过设备标识人员状态可以把人员设备关系都给客户端,客户端自行判断,如果服务端判断复杂度非常高,需要考虑设备不同人使用,设备不同工作任务使用,不能互相冲突,各种中间关联关系。
- 对于批量选择新增的功能简化设计(一对多),正常逻辑(左右选项卡,右边显示已选中,左边显示待选择):集合A(前端)和B(数据库)操作,A-B为新增,B-A为删除,A∩B为不变。简化方法一:数据库先进行对应项全部删除,然后把传过来的选中项全部新增。方案二:客户端选择框只显示可以添加的项,已经添加被关联占用的项不显示,这样客户端提交后就是新增的项,后端只需进行新增操作,如果客户端需要删除就在关联详情列表进行删除操作(union多表关联选出空闲设备)
- 对于批量选择移动的功能简化设计(一对多),关联移动也是可以简化的,三部曲(判断新增、删除、不变)后端实现太复杂。可以前端进行两步接口调用操作: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,后系统自带