映射文件的相关属性
namespace命名空间
作用: 对sql进行分类化管理,理解sql隔离
注意:使用mapper代理方法开发,namespace有特殊重要的作用
<!-- 需求:通过id查询用户表的记录 -->
<select id="findUserById" parameterType="int" resultType="com.chaychan.mybatis.po.User">
SELECT * FROM USER WHERE id=#{value}
</select>
上面代码的解释
id:标识映射文件中的 sql,将sql语句封装到mappedStatement对象中,所以将id称为statement的id
parameterType:指定输入 参数的类型,这里指定int型
resultType:指定sql输出结果 的所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象。
“#{}” 表示一个占位符号
“#{id}” 其中的id表示接收输入 的参数,参数名称就是id,如果输入 参数是简单类型,#{}中的参数名可以任意,可以value或其它名称.
${}的使用
<!-- 通过用户的名称模糊搜索用户,搜索的结果可能有多条 resultType指定就是单条记录所映射的java对象
${}表示拼接sql串,将接收到的内容不加任何修饰拼接在sql中
使用${}拼接sql,会引起sql注入,比如传入的字符串是sql指令(有一定的风险)
${value}:接收输入的内容,如果传入的类型是简单类型,${}中只能用value
-->
<select id="findUserByName" parameterType="java.lang.String" resultType="com.chaychan.mybatis.po.User">
SELECT * FROM USER WHERE USERNAME LIKE '%${value}%';
</select>
以上的模糊搜索会引起sql注入,所以还是使用下面的代码
<select id="findUserByName" parameterType="java.lang.String" resultType="com.chaychan.mybatis.po.User">
<!-- SELECT * FROM USER WHERE USERNAME LIKE '%${value}%';
为了防止sql注入,所以采用下面的方式-->
SELECT * FROM USER WHERE USERNAME LIKE #{value};
</select>
代码中传入参数替代占位符
List<User> list = sqlSession.selectList("test.findUserByName","%小明%");
插入数据
<!-- 添加用户
parameterType:指定输入参数的类型为pojo(包含用户信息)
#{}指定pojo的属性名,就可以接收到相应的属性值,mybatis是通过OGNL获取属性值
-->
<insert id="insertUser" parameterType="com.chaychan.mybatis.po.User">
insert into user(username,birthday,sex,address)
value(#{username},#{birthday},#{sex},#{address})
</insert>
插入数据后拿到自增长的主键的值
<insert id="insertUser" parameterType="com.chaychan.mybatis.po.User">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address)
value(#{username},#{birthday},#{sex},#{address})
</insert>
以上代码的解释
SELECT LAST_INSERT_ID(): 得到刚 insert进去记录的主键值,只适用于自增长主键
keyProperty: 将查询到的主键值设置到parameterType对应的对象的哪个属性中,上述代码是将主键值设置到User对象的id属性中
order: SELECT LAST_INSERT_ID()的执行顺序,相对于insert来说的顺序
resultType: 指定SELECT LAST_INSERT_ID()的结果类型
在插入数据之前通过mysql的uuid()生成主键,然后再将其作为主键值插入
使用mysql的uuid()生成主键
执行过程:
首先通过uuid()得到主键,将主键设置到user对象的id属性中
其次在insert执行时,从user对象中取出id属性值
<insert id="insertUser" parameterType="com.chaychan.mybatis.po.User">
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
SELECT uuid()
</selectKey>
insert into user(id,username,birthday,sex,address)
value(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
mapper的编写
程序员编写mapper接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类的代理对象
开发规范:
1.在mapper.xml中的namespace等于mapper接口的类的全路径
<mapper namespace="com.chaychan.mybatis.mapper.UserMapper">
2.mapper.java接口中的方法名要和mapper.xml中statement的id一致
3.mapper.java接口中的方法的输入参数类型要和mapper.xml中statement的parameterType指定的类型一致
4.mapper.java接口中的方法返回值类型要和mapper.xml中statement的resultType指定的类型一致
如mapper.xml中代码:
<select id="findUserById" parameterType="int" resultType="com.chaychan.mybatis.po.User">
SELECT * FROM USER WHERE id=#{value}
</select>
mapper接口中的方法:
/**根据id查找用户信息*/
public User findUserById(int id) throws Exception;
定义别名
单个别名的定义
SqlMapConfig.xml中配置User对象全路径对应的别名为user
<!-- 定义别名 -->
<typeAliases>
<!--单个别名的定义-->
<typeAlias type="com.chaychan.mybatis.po.User" alias="user"/>
</typeAliases>
mapper.xml中只需使用别名user:
<select id="findUserById" parameterType="int" resultType="user">
SELECT * FROM USER WHERE id=#{value}
</select>
批量别名的定义
SqlMapConfig.xml中配置要包名
<!-- 定义别名 -->
<typeAliases>
<!-- 批量别名的定义
指定包名,mybatis就会自动扫描包中的po类,自动定义别名,别名为类名(首字母大小写都可以)
-->
<package name="com.chaychan.mybatis.po"/>
</typeAliases>
加载映射文件
单个加载映射文件,通过mapper接口加载映射文件
批量加载映射文件,指定mapper接口所在的包名,mybatis会自动加载mapper接口
两种方法都要遵循一定的规范:需要将mapper接口的类名和mapper.xml映射文件名保持一致,且放在同一个目录下
<!-- 加载 映射文件 -->
<mappers>
<mapper resource="sqlmap/User.xml"/>
<!-- <mapper resource="mapper/UserMapper.xml"/> -->
<!-- 单个加载映射文件,通过mapper接口加载映射文件
遵循一定的规范:需要将mapper接口的类名和mapper.xml映射文件名保持一致,且放在同一个目录下
-->
<!-- <mapper class="com.chaychan.mybatis.mapper.UserMapper"/> -->
<!--
批量加载映射文件
指定mapper接口所在的包名,mybatis会自动加载mapper接口
遵循一定的规范:需要将mapper接口的类名和mapper.xml映射文件名保持一致,且放在同一个目录下
-->
<package name="com.chaychan.mybatis.mapper"/>
</mappers>
reusltMap入门
<!-- 定义resultMap
将SELECT id id_,username username_ FROM USER 和User类中的属性作一个映射关系
type:resultMap最终映射的java对象类型,可以使用别名
id:对resultMap的唯一标识
-->
<resultMap type="user" id="userResultMap">
<!-- id表示查询结果集中唯一标识
column:查询出来的列名
property:type指定的pojo类型中的属性名
最终resultMap对column和property作一个映射关系 (对应关系)
-->
<id column="id_" property="id"/>
<!--
result:对普通名映射定义
column:查询出来的列名
property:type指定的pojo类型中的属性名
最终resultMap对column和property作一个映射关系 (对应关系)
-->
<result column="username_" property="username"/>
</resultMap>
<!-- 使用resultMap进行输出映射
resultMap:指定定义的resultMap的id,如果这个resultMap在其它的mapper文件,前边需要加namespace
-->
<select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">
SELECT id id_,username username_ FROM USER WHERE id=#{value}
</select>
定义sql片段
<!-- 定义sql片段
id:sql片段的唯 一标识
经验:是基于单表来定义sql片段,这样话这个sql片段可重用性才高
在sql片段中不要包括 where
-->
<sql id="user_query_where">
<if test="userCustom != null">
<if test="userCustom.sex != null and userCustom.sex != ''">
and sex = #{userCustom.sex}
</if>
<if test="userCustom.username != null and userCustom.username != ''">
and username like '%${userCustom.username}%'
</if>
</if>
</sql>
引用sql片段
<select id="findUserList" parameterType="userQueryVo" resultType="userCustom">
select * from user
<where>
<include refid="user_query_where" />
</where>
</select>
foreach的使用
<if test="ids != null">
<!-- 使用 foreach遍历传入ids
使用实现下边的sql拼接:
AND (id=1 OR id=10 OR id=16)
-->
<foreach collection="ids" item="item_id"
open="AND (" close=")" separator="OR">
id=#{item_id}
</foreach>
<!-- 使用实现下边的sql拼接:
AND id in(1,10,16)
-->
<foreach collection="ids" item="item_id"
open="AND id in(" close=")" separator=",">
#{item_id}
</foreach>
</if>
collection:指定输入对象中集合属性
item:每个遍历生成对象中
open:开始遍历时拼接的串
close:结束遍历时拼接的串
separator:遍历的两个对象中需要拼接的串
延迟加载
sqlConfig.xml中配置开启延迟加载
<!-- 全局配置参数,需要时再设置 -->
<settings>
<!-- 打开延迟加载 的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载即按需要加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 开启二级缓存 -->
<!-- <setting name="cacheEnabled" value="true"/> -->
</settings>
定义一个延迟加载的mapper,如查询订单的时候,延迟加载用户信息,因为orders表中的user_id关联user表的id,当需要获取user对象的时候,就会执行延迟加载。
相当于两步:
1.先查询orders表获取到订单;
2.根据订单对象中user_id查找出相关的user对象
<!-- 延迟加载的resultMap -->
<resultMap type="com.chaychan.mybatis.po.Orders" id="OrdersUserLazyLoadingResultMap">
<!--对订单信息进行映射配置 -->
<id column="id" property="id" />
<result column="user_id" property="userId" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<!-- 实现对用户信息进行延迟加载
select:指定延迟加载需要执行的statement的id(是根据user_id查询用户信息的statement)
要使用userMapper.xml中findUserById完成根据用户id(user_id)用户信息的查询,如果findUserById不在本mapper中需要前边加namespace
column:订单信息中关联用户信息查询的列,是user_id
关联查询的sql理解为:
SELECT orders.*,
(SELECT username FROM USER WHERE orders.user_id = user.id)username,
(SELECT sex FROM USER WHERE orders.user_id = user.id)sex
FROM orders
-->
<association property="user" javaType="com.chaychan.mybatis.po.User" column="user_id"
select="com.chaychan.mybatis.mapper.UserMapper.findUserById"
></association>
</resultMap>
<!-- 查询订单关联查询用户,用户信息需要延迟加载 -->
<select id="findOrdersUserLazyLoading" resultMap="OrdersUserLazyLoadingResultMap">
SELECT * FROM orders
</select>
测试代码
List<Orders> list = ordersMapperCustom.findOrdersUserLazyLoading();
for (Orders orders : list) {
User user = orders.getUser();
System.out.println(user);
}
当获取list的时候,控制台会输入查找orders表的sql语句,当orders对象调用getUser()方法的时候,控制台会输出根据id查找用户的sql语句。
查询缓存
一级缓存是sqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据,不同的sqlSession之间的缓存数据区域(HashMap)是互不影响的。
二级缓存是mapper级别的缓存,多个sqlSession去操作同一个Mapper中的sql语句,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的。
为什么要用缓存?如果缓存中有数据,就不用到数据库中去查找,大大提高系统的性能。
一级缓存的原理
例子:
第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户的信息,如果没有,从数据查询用户信息。得到用户信息后,存储在以及缓存中。
如果sqlSession去执行commit操作(执行插入、更新、删除),将清空sqlSession中的一级缓存,这样做的目的是为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户的信息,缓存中有,直接从缓存中获取用户信息。
二级缓存
mybatis默认开启一级缓存,如果要开启二级缓存,需要在xml中进行配置
原理:
sqlSession1去查询用户id为1的用户信息,查询到用户信息后将数据存储到二级缓存中。
sqlSession2去查询用户id为1的用户信息时,先去缓存中查找,如果存在则直接取出数据。
二级缓存和一级缓存的区别,二级缓存的范围更大,多个sqlSession可以共享一个Mapper的二级缓存区域,不如UserMapper有自己的二级缓存区域,其他mapper也有对应的二级缓存区域,按namespace区分。即每一个namespace的mapper有一个二级缓存区域。
如果两个mapper的namespace相同,这两个mapper执行sql查询到的数据将存在相同的二级缓存区域中。
整合ehcache
配置mapper中cache中的type为ehcache对cache接口的实现类
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
导入mybatis-ehcache-1.0.2.jar,ehcache-core-2.6.5.jar