公司目前使用springmvc+maven+mybatis开发项目,用到了数据库中一对多的关系,所以做此总结。
mapper代理开发dao
使用mapper代理开发dao时,需要编写mapper.xml映射文件以及mapper接口。
<mapper namespace="com.zyuc.fw.basic.service.repository.mapper.IFwAclPolicyMapper">
<insert id="insert" parameterType="AclPolicy" useGeneratedKeys="true" keyProperty="aclId">
insert into fw_acl_policy (vfw_id, acl_number) values (#{vfwId},#{aclNumber})
</insert>
<delete id="delete" parameterType="AclPolicy">
delete from fw_acl_policy
<where>
<if test="aclNumber != null and aclNumber != ''">
fw_acl_policy.acl_number=#{aclNumber}
</if>
</where>
</delete>
定义好了mapper.xml映射文件后,接下来就要编写mapper接口了,编写mapper接口要遵循以下四个开发规范:
- 在mapper.xml中,使namespace等于mapper接口的地址(完全限定名)
- mapper.java接口中的方法名和mapper.xml中statement的id一致
- mapper.java接口中方法的输入参数类型和mapper.xml中statement的parameterType指定的类型一致
- mapper.java接口中方法返回值类型和mapper.xml中statement的resultType指定的类型一致
根据这四条开发规范,我们来完成mapper接口
public interface IRepository<T> {
int insert(T var1);
int update(T var1);
int delete(T var1);
T get(T var1);
PageWrapper<T> page(PageParam var1, T var2);
}
public interface IFwAclPolicyMapper extends IRepository<AclPolicy> {
}
mybatis全局配置文件
配置内容 作用
< properties> 用来加载属性文件
< settings> 用来设置全局参数
< typeAliases> 用来设置类型的别名
< typeHandlers> 用来设置类型处理器
< objectFactory> 用来设置对象工厂
< plugins> 用来设置插件
< environments> 用来设置mybatis的环境
< mappers> 用来配置映射文件
需要注意的是,配置项需要严格按照顺序来配置。
mapper.xml中定义了许多参数类型。类型全路径,有时候会很长,不方便进行开发,那么我们就可以可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名来定义,方便开发。
<!-- 别名的定义 -->
<typeAliases>
<!-- 针对单个别名的定义。type:类型的路径; alias:别名 -->
<typeAlias type="mybatis.po.User" alias="user"/>
</typeAliases>
然后输入参数或者输出结果为mybatis.po.User时,就可以用user来代替了。
数据库一对多关系
这里aclPolicy对应多条aclRule。aclRule的查找实现如下:
<resultMap type="AclRule" id="AclRuleResultMap">
<!-- 配置acl规则的明细信息 -->
<id column="acl_id" property="aclId"/>
<result column="global_rule_id" property="globalRuleId"/>
<result column="protocol" property="protocol"/>
<result column="source_ip" property="sourceIp"/>
<result column="dest_ip" property="destIp"/>
<result column="action" property="action"/>
</resultMap>
<select id="getAclRule" parameterType="String" resultMap="AclRuleResultMap">
select * from fw_acl_rule where global_acl_id=#{global_acl_id}
</select>
//此处定义aclPolicy的resultmap,collection可以包含一个list
<resultMap type="AclPolicy" id="AclPolicyResultMap">
<!--定义aclpolicy公共参数-->
<id column="acl_id" property="aclId"/>
<result column="vfw_id" property="vfwId"/>
<result column="acl_number" property="aclNumber"/>
<!-- 配置acl规则的明细信息 -->
<collection property="aclRules" column="global_acl_id" javaType="ArrayList" ofType="AclRule" select="getAclRule"/>
</resultMap>
<sql id="Acl_Policy_Column">
select
fw_acl_policy.*,
fw_acl_rule.global_rule_id,
fw_acl_rule.global_acl_id,
fw_acl_rule.protocol,
fw_acl_rule.source_ip,
fw_acl_rule.dest_ip,
fw_acl_rule.action
from
fw_acl_policy LEFT JOIN fw_acl_rule ON fw_acl_policy.global_acl_id = fw_acl_rule.global_acl_id
</sql>
<select id="get" parameterType="AclPolicy" resultMap="AclPolicyResultMap">
<include refid="Acl_Policy_Column" />
<where>
<if test="vfwId != null and vfwId != ''">
fw_acl_policy.vfw_id=#{vfwId}
</if>
<if test="aclNumber != null and aclNumber != ''">
AND fw_acl_policy.acl_number=#{aclNumber}
</if>
</where>
</select>
这样进行查询aclPolicy时,就会查出aclRule的list。
结果如下:
{"body":{"acl_number":"10","acl_rules":
[{"action":true,"dest_ip":"2.2.2.2","global_acl_id":"1","global_rule_id":"1","protocol":"17","rule_id":"1","source_ip":"1.1.1.1"},
{"action":true,"dest_ip":"4.4.4.4","global_acl_id":"1","global_rule_id":"2","protocol":"16","rule_id":"2","source_ip":"3.3.3.3"}],
"global_acl_id":"1","vfw_id":"1"},
"success":true}
缓存
序列化缓存
- 用途是先将对象序列化成2进制,再缓存,好处是将对象压缩了,省内存
- 坏处是速度慢了
mybatis一级缓存
mybatis默认支持一级缓存,不需要另外配置
在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,如果不采取一些措施的话,每一次查询都会查询一次数据库,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。
为了解决这一问题,减少资源的浪费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了
一级缓存被清除时机
a. MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
b. 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;
c. 如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;
d.SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用;
mybatis二级缓存
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。如果开启了二级缓存,那么在关闭sqlsession后,会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。
MyBatis查询数据的顺序是:
二级缓存 ———> 一级缓存——> 数据库
要开启二级缓存步骤:
1.打开二级缓存总开关
打开总开关,只需要在mybatis总配置文件中加入一行设置
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
2.打开需要使用二级缓存的mapper的开关
在需要开启二级缓存的mapper.xml中加入caceh标签
<cache/>
3.POJO序列化
让需要使用二级缓存的POJO类实现Serializable接口,如
public class User implements Serializable {
通过之前三步操作就可以使用二级缓存了。测试一下
@Test
public void testFindUsersCache() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.findUsers();
//关闭sqlsession
sqlSession.close();
//通过sqlsessionFactroy创建一个新的sqlsession
sqlSession = sqlSessionFactory.openSession();
//获取mapper对象
userMapper = sqlSession.getMapper(UserMapper.class);
users = userMapper.findUsers();
System.out.println(users);
}
还有一个问题,之前说了,即使开启了二级缓存,不同的sqlsession之间的缓存数据也不是想互访就能互访的,必须等到sqlsession关闭了以后,才会把其一级缓存中的数据写入二级缓存。如果注释sqlSession.close();则下次查询不会从缓存获取。