mybatis
1.mybatis概述
1. 1 什么是mybatis
1:Hibernate就是一个持久层的的框架。对JDBC做了轻量级封装
2:Mybatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射
1.2 mybatis概述
1:它是基于Java编写的持久层框架,使开发者不必关心传统jdbc的api,只关心sql语句本身
2:mybatis 通过 xml或注解的方式将要执行的各种 statement 配置起来
3:采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作
2.mybatis的CRUD操作
2.1 mybatis查询所有的步骤
1.创建工程
2.导入maven坐标
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
3.创建实体类
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
}
4.导入log4j.properties文件
查询所有
5.创建SqlMapConfig.xml(省略)
<typeAliases>用于指定别名
<environments>配置环境
<environment>
<transactionManager type="JDBC">配置事务管理策略
<dataSource type="POOLED">配置数据区连接方式
<property name="" value=""/>配置数据库连接参数
<mappers>
<package name="com.cyannote.dao"/>用于指定dao接口所在的包
6.创建UserDao.java
public interface UserDao {
/**
* 查询所有用户
* @return
*/
List<User> findAll();
}
7.创建UserDao.xml
<mapper namespace="com.cyannote.dao.UserDao">
<!--查询所有-->
<select id="findAll" resultType="com.cyannotea.domain.User">
select * from user
</select>
</mapper>
8.创建测试类MybatisTest .java(省略)
2.2 #{}与${}的区别
#{}表示一个占位符号
通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
表
示
拼
接
s
q
l
串
通
过
{}表示拼接sql串 通过
表示拼接sql串通过{}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换,
可
以
接
收
简
单
类
型
值
或
p
o
j
o
属
性
值
,
如
果
p
a
r
a
m
e
t
e
r
T
y
p
e
传
输
单
个
简
单
类
型
值
,
{}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,
可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,{}括号中只能是value。
2.3 SqlMapConfig.xml配置文件
SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)
SqlMapConfig.xml配置文件
<properties resource="jdbcConfig.properties"></properties>
settings(全局配置参数)
typeAliases(类型别名)
<!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
<!--<package name="com.cyannote.domain"></package>-->
</typeAliases>
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
<package name=""/>
注册指定包下的所有mapper接口
如:<package name="com.caynnote.dao"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
应用场景:即可以使用XML配置文件,也可以使用注解。
2.4 Mybatis映射文件的SQL深入
动态SQL的标签
<!--根据条件查询-->
<select id="findByCondition" parameterType="user" resultMap="userMap">
select * from user where 1=1
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</select>
动态SQL之标签
<!--where标签的使用-->
<select id="findByCondition" parameterType="user" resultMap="userMap">
select * from user
<where>
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</where>
</select>
动态标签之标签
<!--foreach标签的使用-->
<select id="findInIds" parameterType="queryVo" resultMap="userMap">
select * from user
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open=" and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
3.mybatis的多表查询
3.1 多对一(一对一):
方案一:创建新的javabean(包含两张表的字段)
第一步:sql语句
SELECT a.*,u.username,u.address FROM account a,USER u WHERE u.id = a.uid
第二步:在com.cyanote.domain中创建AccountUser.java
创建一个新的javabean
public class AccountUser extends Account implements Serializable {
private String username;
private String address;
}
第三步:AccountDao.java
/**
*查询账号和客户的信息
*/
List findByAccountUser();
第四步:AccountDao.xml
<!-- 查询所有 -->
<select id="findByAccountUser" resultType="com.cyannote.domain.AccountUser">
SELECT a.*,u.username,u.address FROM account a,user u WHERE u.id = a.uid
</select>
第五步:MybatisTest.java
方案二:直接用用Account对象封装
第一步:sql语句
select u.*,a.id as aid,a.uid,a.money from account a,user u where u.id = a.uid
第二步:修改Account.java
在Account中封装User
public class Account implements Serializable{
private Integer id;
private Integer uid;
private Double money;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
第三步:AccountDao.java
/**
*查询账号和客户的信息(方案二:直接用Account对象封装)
*/
List<Account> findByAccountUser2();
第四步:AccountDao.xml
<!--定义resultMap对象,用来封装账号信息-->
<resultMap id="accountMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--association用来关联对象(用于多对一,或一对一),property代表加载对象,javaType代表加载对象的数据类型,可以写成com.cyannote.domain.User-->
<association property="user" javaType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
</association>
</resultMap>
<!-- 查询所有(方案二:直接用Account对象封装) -->
<select id="findByAccountUser2" resultMap="accountMap">
select u.*,a.id as aid,a.uid,a.money from account a,user u where u.id = a.uid
</select>
第五步:MybatisTest.java
4.mybatis的延迟加载策略
4.1 什么是延迟加载
什么是延迟加载
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载
不管用不用,只要一调用方法,马上发起查询。
应用场景:
在对应的四种表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载。(建议)
多对一,一对一:通常情况下我们都是采用立即加载。(建议)
4.2 配置延迟加载
配置SqlMapConfig.xml:****(全局配置)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbcConfig.properties"></properties>
<!--需要配置延迟加载策略-->
<settings>
<!--打开延迟加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--将积极加载改为消息加载,即按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
配置AccountDao.xml:(局部配置)
AccountDao.xml中使用fethcType属性用来控制延迟检索和立即检索:
<!--fetchType:
eager:立即检索
lazy:延迟检索
-->
<association property="user" javaType="user" select="com.cyannote.dao.UserDao.findById" column="uid" fetchType="eager">
</association>
只配置一处就可实现延迟加载,若两处都配置,局部配置优先级别更高.
5.mybatis缓存
Mybatis中缓存分为一级缓存,二级缓存
一级缓存:是SqlSession级别的缓存(线程级别的缓存)
二级缓存:是SqlSessionFactory级别的缓存(进程级别的缓存)
一个SqlSessionFactory存在多个SqlSession。
5.1 一级缓存(SqlSession)
它指的是Mybatis中SqlSession对象的缓存。默认是开启SQLSession缓存的.
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。一级缓存中存放的是一个对象.
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用,如果没有再查询数据库。
一级缓存是SqlSession 范围的缓存,当调用SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
第一次发起查询用户id 为1 的用户信息,先去找缓存中是否有id 为1 的用户信息,如果没有,从数据库查询用户信息。 得到用户信息,将用户信息存储到一级缓存中。 如果sqlSession 去执行commit 操作(执行插入、更新、删除),清空SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户id 1 id 1
5.2 二级缓存(SqlSessionFactory)
它指的是Mybatis中SqlSessionFactory对象的级别缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
因为cacheEnabled 的取值默认就为true,所以这一步可以省略不配置。为true 代表开启二级缓存; 为false 代表不开启二级缓存。
<!--需要配置延迟加载策略-->
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
第二步:让当前的映射文件支持二级缓存(在UserDao.xml中配置)
标签表示当前这个mapper 映射将使用二级缓存,区分的标准就看mapper 的namespace 值。
<mapper namespace="com.cyannote.dao.UserDao">
<cache/>
</mapper>
第三步:让当前的操作支持二级缓存(在select标签中配置)
将UserDao.xml 映射文件中的标签中设置useCache=”true”代表当前这个statement 要使用二级缓存,如果不使用二级缓存可以设置为false。
注意:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="com.cyannote.domain.User" useCache="true">
select * from user where id = #{uid}
</select>
二级缓存结构图
经过测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。
但是查询到的2个对象不一致,原理是二级缓存中存放的是对象的散装数据,每次查询的时候需要重新封装实体对象。
考虑二级缓存的应用场景:
适应放置到二级缓存的数据:经常不会发生变化的数据,例如地区编码
不适合放置到二级缓存的数据:经常变化的数据, 财务数据
6.mybatis注解开发
加载注解依赖(pom.xml)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
配置映射(SqlMapConfig.xml)
<!-- 指定带有注解的dao接口所在位置 -->
<mappers>
<mapper class="com.cyannote.dao.UserDao"></mapper>
</mappers>
6.1 mybatis的注解说明
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态SQL映射
6.2 使用注解开发
使用注解进行CRUD操作
// 查询所有
@Select(value = "select * from account")
List<Account> findAll();
// 增加一条记录
@Insert(value = "insert into account (uid,money) values (#{uid},#{money})")
void save(Account account);
// 删除一条记录
@Delete(value = "delete from account where id = #{id}")
void delete(int id);
// 修改一条记录
@Update(value = "update account set uid = #{uid},money = #{money} where id= #{id}")
void update(Account account);
使用注解实现复杂关系映射开发
@one:多对一和一对一
@Select(value = "SELECT * from account")
@Results(id = "AccountUser", value = {
@Result(id = true, property = "id", column = "id"),
@Result(property = "uid", column = "uid"),
@Result(property = "money", column = "money"),
@Result(property = "user", column = "uid", one = @One(select = "com.cyannote.dao.UserDao.findByUId"))
})
List<Account> findAccountUser();
@many:一对多和多对多
@Select(value = "select * from user")
@Results(id = "UserAccount",value = {
@Result(id = true , property = "id" , column = "id"),
@Result(property = "username",column = "username"),
@Result(property = "sex" , column = "sex"),
@Result(property = "birthday" , column = "birthday"),
@Result(property = "address" , column = "address"),
@Result(property = "accounts",column = "id",many = @Many(select = "com.cyannote.dao.AccountDao.findById"))
})
List<User> findUserAccount();
使用联合查询的SQL语句
@Select(value = "SELECT u.*,a.id aid,a.UID,a.MONEY FROM USER u,account a WHERE a.uid = u.id")
@Results(id = "AccountUser2",value = {
@Result(id = true,property = "id",column = "aid"),
@Result(property = "uid",column = "uid"),
@Result(property = "money",column = "money"),
@Result(property = "user.id",column = "id"),
@Result(property = "user.username",column = "username"),
@Result(property = "user.sex",column = "sex"),
@Result(property = "user.birthday",column = "birthday"),
@Result(property = "user.address",column = "address"),
})
List<Account> findAccountUser2();
使用注解配置二级缓存
第一步:配置SqlMapConfig.xml
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
第二步:配置UserDao.xml
@CacheNamespace(blocking = true) // 使用二级缓存
public interface UserDao {
}