JAVA框架02 -- MyBatis

框架

什么是框架?

  • 它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题。
  • 使用框架的好处:框架封装了很多的细节,使开发者可以使用极简的方式实现功能。大大提高开发效率。

三层架构

  • 表现层:是用于展示数据的
  • 业务层:是处理业务需求
  • 持久层:是和数据库交互的

三层架构

持久层技术解决方案

JDBC技术
  • Connection

  • PreparedStatement

  • ResultSet

Spring的JdbcTemplate
  • Spring中对jdbc的简单封装
Apache的DBUtils
  • 它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装
总结
  • JDBC是规范

  • Spring的JdbcTemplate和Apache的DBUtils都只是工具类

持久层纵览

mybatis的概述

描述

  • mybatis是一个持久层框架,用java编写的。
  • 它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程
  • 它使用了ORM思想实现了结果集的封装。

ORM

  • Object Relational Mappging 对象关系映射:就是把数据库表和实体类及实体类的属性对应起来让我们可以操作实体类就实现操作数据库表。

user – User id – UserId

  • 需要实体类中的属性和数据库表的字段名称保持一致。

user –User. id – id

mybatis的入门

mybatis的环境搭建
  • 第一步:创建maven工程并导入坐标
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
  • 第二步:创建实体类和dao的接口
  • 第三步:创建Mybatis的主配置文件 SqlMapConifg.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">

<!--mybatis主配置文件 -->
<configuration>
<!--配置环境 -->
<environments default="mysql">
<!-- 配置mysql环境 -->
<environment id="mysql">
<!--配置事务类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="drive" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://locahist:3306/mybatisbd"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>

<!--指定映射配置文件的位置 映射配置文件指每个dao独立的配置文件 -->
<mappers>
<mapper resource="com/jwang/dao/UserDao.xml"/>
</mappers>
</configuration>
  • 第四步:创建映射配置文件 UserDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.jwang.dao.UserDao">
<!-- 配置查询所有 id取在userDao中对应的方法名-->
<select id="findAll"> select * from user;</select>
</mapper>
环境搭建的注意事项:
  • 第一个:创建UserDao.xml 和 UserDao.java时名称是为了保持一致。

    在Mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper
    所以:UserDao 和 UserMapper是一样的

  • 第二个:在idea中创建目录的时候,它和包是不一样的

    包在创建时:com.jwang.dao它是三级结构

    目录在创建时:com.jwang.dao是一级目录

  • 第三个:mybatis的映射配置文件位置必须和dao接口的包结构相同

  • 第四个:映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名

  • 第五个:映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名

当我们遵从了第三,四,五点之后,我们在开发中就无须再写dao的实现类。

mybatis的入门案例

mybatis分析

注意事项
  • 不要忘记在映射配置中告知mybatis要封装到哪个实体类中

  • 配置的方式:指定实体类的全限定类名

public class MybatisTest {
public static void main(String[] args) throws IOException {
//1.读取配置文件
/*
1.1 绝对路径和相对路径写法不靠谱
1.2 其他加载方法:
使用类加载器
使用SeevletContext对象的getRealPath()
*/
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建一个sqlsessionfactory工厂
/*
2。1:创建工厂,mybatis使用构建者模式 用构建者对in进行build 把对象创建细节隐藏。使用者直接调用方法拿到对象
*/
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3。使用工厂生产一个sqlSession对象
/*
3。1:生产一个sqlSession对象使用工厂模式:降低类之间的依赖关系即解藕
*/
SqlSession sqlSession = factory.openSession();
//4。使用sqlsession创建dao接口的代理对象
/*
4。1:创建dao接口实现类 使用代理模式:不修改源码的基础上对已有方法的增强
*/
UserDao userDao = sqlSession.getMapper(UserDao.class);
//5。使用代理对象执行方法
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6。释放资源
sqlSession.close();
in.close();
}
}

注解

  • 不需要编写UserDao.xml 可以直接在接口相关方法上加注解sql语句
mybatis基于注解的入门案例:

案例分析

  • 把UserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句
  • 同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。

<mapper class="com.jwang.dao.UserDao"/>
  • 明确:

我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。

不管使用XML还是注解配置。

但是Mybatis它是支持写dao实现类的。

public interface UserDao {

/**
* 查询所有操作
* @return
*/
@Select("select * from user")
List<User> findAll();
}

自定义Mybatis的分析:

查询所有分析

  • mybatis在使用代理dao的方式实现增删改查时做什么事呢?
  • 只有两件事:

第一:创建代理对象

第二:在代理对象中调用selectList

代理dao

  • 自定义mybatis能通过入门案例看到类

class Resources

class SqlSessionFactoryBuilder

interface SqlSessionFactory

interface SqlSession

CRUD操作

#UserDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jwang.dao.UserDao">

<!-- 查询所有 -->
<select id="findAll" resultType="com.jwang.domain.User">
select * from user;
</select>

<!-- 保存用户 -->
<insert id="saveUser" parameterType="com.jwang.domain.User">
<!-- 配置插入操作后,获取插入数据的id -->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday});
</insert>

<!-- 更新用户 -->
<update id="updateUser" parameterType="com.itheima.domain.User">
update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}
</update>

<!-- 删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id = #{uid}
</delete>

<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="com.jwang.domain.User">
select * from user where id = #{uid}
</select>

<!-- 根据名称模糊查询 -->
<select id="findByName" parameterType="string" resultType="com.jwang.domain.User">
select * from user where username like #{name}
</select>

<!-- 获取用户的总记录条数 -->
<select id="findTotal" resultType="int">
select count(id) from user;
</select>
</mapper>
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">

<!--mybatis主配置文件 -->
<configuration>
<!--配置文件路径 -->
<properties resource="jdbcConfig.properties"></properties>

<typeAliases>
<!--指定配置别名的包,全部实体类都会注册别名,类名就是别名,不区分大小写 -->
<package name="com.jwang.domain"/>
</typeAliases>

<!--配置环境 -->
<environments default="mysql">
<!-- 配置mysql环境 -->
<environment id="mysql">
<!--配置事务类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

<!--指定映射配置文件的位置 映射配置文件指每隔dao独立的配置文件
如果是注解配置 此处应该使用class属性指定被注解的dao全限定类名-->
<mappers>
<!--URL: 协议 主机 端口 URI 统一资源定位符 URI:统一资源标示符 唯一定位应用的资源 -->
<!-- <mapper resource="com/jwang/dao/UserDao.xml"/>-->
<!-- <mapper class="com.jwang.dao.UserDao"/>-->
<!--dao接口所在的包 上面的就不需要写 -->
<package name="com.jwang.dao"/>
</mappers>

</configuration>

开发流程

过程


#main测试类

public class MybatisTest {

private InputStream in;
private SqlSession sqlSession;
private UserDao userDao;

@Before
public void init() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建一个sqlsessionfactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3。使用工厂生产一个sqlSession对象
sqlSession = factory.openSession();
//4。使用sqlsession创建dao接口的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}

@After
public void destory() throws IOException {
sqlSession.commit();
sqlSession.close();
in.close();
}

public static void main(String[] args) throws IOException {
//1.读取配置文件
/*
1.1 绝对路径和相对路径写法不靠谱
1.2 其他加载方法:
使用类加载器
使用SeevletContext对象的getRealPath()
*/
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建一个sqlsessionfactory工厂
/*
2。1:创建工厂,mybatis使用构建者模式 用构建者对in进行build 把对象创建细节隐藏。使用者直接调用方法拿到对象
*/
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3。使用工厂生产一个sqlSession对象
/*
3。1:生产一个sqlSession对象使用工厂模式:降低类之间的依赖关系即解藕
*/
SqlSession sqlSession = factory.openSession();
//4。使用sqlsession创建dao接口的代理对象
/*
4。1:创建dao接口实现类 使用代理模式:不修改源码的基础上对已有方法的增强
*/
UserDao userDao = sqlSession.getMapper(UserDao.class);
//5。使用代理对象执行方法
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6。释放资源
sqlSession.close();
in.close();
}

@Test
public void testFind(){
final List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testSave(){
User user = new User();
user.setUsername("mada");
user.setAddress("asa");
user.setSex("男");
user.setBirthday(new Date());

userDao.saveUser(user);

}

@Test
public void testUpdate(){
User user = new User();
user.setId(51);
user.setUsername("mada");
user.setAddress("asa");
user.setSex("女");
user.setBirthday(new Date());

userDao.updateUser(user);

}

@Test
public void testDelete(){
userDao.deletrUser(51);
}

@Test
public void testFindOne(){
final User user = userDao.findById(48);
System.out.println(user);

}

@Test
public void testFindByName(){
final List<User> users = userDao.findByName("%王%");
for (User user : users) {
System.out.println(user);
}
}

@Test
public void testFindTotal() {
int total = userDao.findTotal();
System.out.println(total);
}

/**
* queryvo
*/
@Test
public void testFindByName01(){
QueryVo queryVo = new QueryVo();
User user =new User();
user.setUsername("%王%");
queryVo.setUser(user);
final List<User> userByVo = userDao.findUserByVo(queryVo);
for (User u : userByVo) {
System.out.println(u);
}
}
}

自定义nybatis

模糊查询

MyBatis连接池

POOLED原理

mybatis_pooled

连接池

配置方式

<environments default="mysql">
<!-- 配置mysql环境 -->
<environment id="mysql">
<!--配置事务类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
配置的位置
  • 主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。
type属性的取值
  • POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现(会在池中获取链接最后归还)

满足的要求

  • UNPOOLED 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。(直接创建链接,最后关闭)
  • JNDI 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。

区别

注意
  • 如果不是web或者maven的war工程,是不能使用的。我们使用的是tomcat服务器,采用连接池就是dbcp连接池。

MyBatis动态sql语句

if与where标签

<!--    <select id="findUserByCondition" resultType="com.jwang.domain.User" parameterType="User">-->
<!-- select * from user where 1=1-->
<!-- <if test="username != null">-->
<!-- and username = #{username}-->
<!-- </if>-->
<!-- <if test="sex != null">-->
<!-- and sex = #{sex}-->
<!-- </if>-->

<!-- </select>-->

<select id="findUserByCondition" resultType="com.jwang.domain.User" parameterType="User">
select * from user
<where>
<if test="username != null">
and username = #{username}
</if>
<if test="sex != null">
and sex = #{sex}
</if>
</where>
</select>
@Test
public void testFindByCondition(){
User user = new User();
user.setUsername("老王");
user.setSex("男");
List<User> users =userDao.findUserByCondition(user);
for (User user1 : users) {
System.out.println(user1);
}
}

foreach和sql标签

<select id="findUserByInIds" resultType="com.jwang.domain.User" parameterType="QueryVo">
select * from user
<where>
<if test="ids != null and ids,size()>0">
<foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
  • sql代码片段抽取
<sql id="defalutUser">
select * from user
</sql>

在使用该语句的地方
<include refid="defalutUser"></include>

多表查询

一对一

<mapper namespace="com.jwang.dao.IAcountDao">
<select id="findAll" resultType="acount">select * from account</select>

<!-- <select id="findAllAcount" resultType="acountuser">-->
<!-- select a.*, u.username, u.address from account a, user u where u.id = a.uid-->
<!-- </select>-->

<!-- 定义user和acount的封装-->
<resultMap id="acountUserMap" type="acount">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>

<!--一对一关系映射 -->
<association property="user" column="uid" javaType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</association>
</resultMap>

<select id="findAllAcount" resultMap="acountUserMap">
select u.*, a.id as aid, a.uid, a.money from account a, user u where u.id = a.uid
</select>
acount实体类

public class Acount 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;
}

一对多

public class User implements Serializable {
private String username;
private String sex;
private String address;
private Date birthday;
private Integer id;

//一对多,主表实体应该包含从表实体的集合引用
private List<Acount> acounts;

public List<Acount> getAcounts() {
return acounts;
}
<!--定义User的resultMap -->
<resultMap id="userAcountMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>

<!--一对多 user对象中的acount对象的映射 pro数据库字段 co对象属性-->
<collection property="acounts" ofType="acount">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>

<select id="findAll" resultMap="userAcountMap">
select * from user u left outer join account a on u.id = a.uid
</select>

多对多

角色–用户
public class Role implements Serializable {
private Integer roleId;
private String roleName;
private String roleDesc;

//多对多关系映射 一个角色可以赋予多个用户
private List<User> user;

public List<User> getUser() {
return user;
}

public void setUser(List<User> user) {
this.user = user;
}
<mapper namespace="com.jwang.dao.IRoleDao">
<resultMap id="roleMap" type="role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="ROLE_NAME"></result>
<result property="roleDesc" column="ROLE_DESC"></result>

<collection property="user" ofType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</collection>


</resultMap>
<select id="findAll" resultMap="roleMap">
select u.*, r.ID as rid, r.ROLE_NAME,r.ROLE_DESC
from role r left outer join user_role ur on r.id = ur.RID
left outer join user u on u.id = ur.UID;
</select>
</mapper>

用户–角色

public class User implements Serializable {
private String username;
private String sex;
private String address;
private Date birthday;
private Integer id;

//多对多关系映射 一个用户具备多个角色

private List<Role> roles;

public List<Role> getRoles() {
return roles;
}
<!--定义User的resultMap -->
<resultMap id="userAcountMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>

<collection property="roles" ofType="Role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="ROLE_NAME"></result>
<result property="roleDesc" column="ROLE_DESC"></result>
</collection>
</resultMap>

<select id="findAll" resultMap="userAcountMap">
select u.*, r.ID as rid, r.ROLE_NAME,r.ROLE_DESC
from user u left outer join user_role ur on u.id = ur.UID
left outer join role r on r.id = ur.RID;
</select>

JNDI

  • JNDI:Java Naming and Directory Interface。是SUN公司推出的一套规范,属于JavaEE技术之一。目的是模仿windows系统中的注册表。在服务器中注册数据源:

延迟加载和立即加载

基础知识

延迟加载
  • 在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
  • 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速
    度要快。

延迟加载

立即加载
  • 不管用不用,只要一调用方法,马上发起查询。
四种表间关系的加载方法
  • 在对应的四种表关系中:一对多,多对一,一对一,多对多

  • 一对多,多对多:通常情况下我们都是采用延迟加载。

  • 多对一,一对一:通常情况下我们都是采用立即加载。

使用 assocation 实现延迟加载

<mapper namespace="com.itheima.dao.IAccountDao">
<!-- 建立对应关系 -->
<resultMap type="account" id="accountMap">
<id column="aid" property="id"/> <result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 它是用于指定从表方的引用实体属性的 --> <association property="user" javaType="user" select="com.jwang.dao.IUserDao.findById" column="uid"> </association>
</resultMap> <select id="findAll" resultMap="accountMap">
select * from account
</select> </mapper>

select: 填写我们要调用的 select 映射的 id
column : 填写我们要传递给 select 映射的参数
  • 开启Mybatis的延迟加载策略
我们需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置。 
<!-- 开启延迟加载的支持 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/> </settings>

使用 Collection 实现延迟加载

< resultMap type="user" id="userMap"> 
<id column="id" property="id"></id> <result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/> <result column="birthday" property="birthday"/>

<!-- collection是用于建立一对多中集合属性的对应关系
ofType 用于指定集合元素的数据类型 select 是用于指定查询账户的唯一标识(账户的 dao 全限定类名加上方法名称)
column 是用于指定使用哪个字段的值作为条件查询
--> <collection property="accounts" ofType="account"
select="com.itheima.dao.IAccountDao.findByUid" column="id"> </collection>
</resultMap> <!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select * from user </select>

<collection>标签:主要用于加载关联的集合对象 select 属性:用于指定查询 account 列表的 sql 语句,所以填写的是该 sql 映射的 id
column 属性:用于指定 select 属性的 sql 语句的参数来源,上面的参数来自于 user 的 id 列,所以就写成 id 这一 个字段名了

Mybatis 缓存

概念(userCache)

  • 像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提 高性能。
  • Mybatis 中缓存分为一级缓存,二级缓存
  • 什么是缓存 :存在于内存中的临时数据。
  • 为什么使用缓存:减少和数据库的交互次数,提高执行效率。
适用于缓存:
  • 经常查询并且不经常改变的。
  • 数据的正确与否对最终结果影响不大的。
不适用于缓存:
  • 经常改变的数据
  • 数据的正确与否对最终结果影响很大的。

例如:商品的库存,银行的汇率,股市的牌价。

Mybatis 一级缓存

  • 一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。
  • 它指的是Mybatis中SqlSession对象的缓存。
  • 如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
原理
  • 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
  • 该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
    查询是否有,有的话直接拿出来用。
  • 当SqlSession对象消失时,mybatis的一级缓存也就消失了。
<mapper namespace="com.itheima.dao.IUserDao">
<!-- 根据 id 查询 -->
<select id="findById" resultType="UsEr" parameterType="int" useCache="true">
select * from user where id = #{uid} </select>
</mapper>
测试一级缓存
/**
* 测试一级缓存 */ @Test
public void testFirstLevelCache(){
User user1 = userDao.findById(41); System.out.println(user1);
// sqlSession.close();
//再次获取 SqlSession 对象 // sqlSession = factory.openSession();
sqlSession.clearCache();//此方法也可以清空缓存 userDao = sqlSession.getMapper(IUserDao.class);

User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1 == user2); } /**

* 测试缓存的同步
*/
@Test public void testClearlCache(){
//1.根据 id 查询用户
User user1 = userDao.findById(41); System.out.println(user1); //2.更新用户信息
user1.setUsername("update user clear cache");
user1.setAddress("北京市海淀区");
userDao.updateUser(user1); //3.再次查询 id 为 41 的用户 User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1 == user2);
} 当执行 sqlSession.close()后,再次获取 sqlSession 并查询 id=41 的 User 对象时,又重新执行了 sql 语句,从数据库进行了查询操作。

Mybatis 二级缓存

  • 它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

二级缓存的使用步骤:
  • 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/> </settings>
因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。
true 代表开启二级缓存;为 false 代表不开启二级缓存。
  • 第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IUserDao">
<!-- 开启二级缓存的支持 --> <cache></cache>
</mapper>
  • 第三步:让当前的操作支持二级缓存(在select标签中配置)
<!-- 根据 id 查询 -->
<select id="findById" resultType="user" parameterType="int" useCache="true"> 	select * from user where id = #{uid}
</select>

将 UserDao.xml 映射文件中的<select>标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false

注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。
二级缓存测试
/**
* @author * @Company
*/
public class SecondLevelCacheTest { private InputStream in;
private SqlSessionFactory factory;
@Before//用于在测试方法执行之前执行

public void init()throws Exception{
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");

//2.获取 SqlSessionFactory
factory = new SqlSessionFactoryBuilder().build(in); }
@After//用于在测试方法执行之后执行
public void destroy()throws Exception{
in.close(); } /**
* 测试一级缓存 */
@Test public void testFirstLevelCache(){
SqlSession sqlSession1 = factory.openSession(); IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
User user1 = dao1.findById(41);
System.out.println(user1);
sqlSession1.close();//一级缓存消失
SqlSession sqlSession2 = factory.openSession(); IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
User user2 = dao2.findById(41);
System.out.println(user2);
sqlSession2.close();

System.out.println(user1 == user2);
}
}

Mybatis 注解开发

常用注解

  • @Insert:实现新增
  • @Update:实现更新
  • @Delete:实现删除
  • @Select:实现查询
  • @Result:实现结果集封装
  • @Results:可以与@Result 一起使用,封装多个结果集
  • @ResultMap:实现引用@Results 定义的封装
  • @One:实现一对一结果集封装
  • @Many:实现一对多结果集封装
  • @SelectProvider: 实现动态SQL映射
  • @CacheNamespace:实现注解二级缓存的使用

使用

public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;


//一对多 关系映射, 一个用户对应多个账户
//在一方写多方的列表对象
private List<Account> accounts;

public List<Account> getAccounts() {
return accounts;
}

<?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="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<!--实体类别名-->
<package name="com.jwang.domain"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<!--设置mysql-->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--dao-->
<package name="com.jwang.dao"/>
</mappers>
</configuration>
@CacheNamespace(blocking = true)  //开启二级缓存
public interface IUserDao {

@Select("select * from user")
@Results(id = "userMap",value = {
@Result(id = true, column = "id", property = "id"),
@Result(column = "username", property = "username"),

@Result(property = "accounts", column = "id", many = @Many(
select = "com.jwang.dao.IAccountDao.findAccountByUid",
fetchType = FetchType.LAZY
))
}) //一对多,采用延迟加载
List<User> findAll();

@Insert("insert into user(username, address, sex, birthday)values(#{username},#{address},#{sex},#{birthday})")
void saveUser(User user);

@Delete("delete from user where id = #{id}")
@ResultMap("userMap") //@ResultMap(value = {"userMap"})
void deleteUser(Integer userid);

@Select("select * from user where id = #{id}")
@ResultMap("userMap")
User findById(Integer userId);
}

在多方实体类写一方的实体类对象引用
public interface IAccountDao {

@Select("select * from account")
@Results(id = "accountMap", value = {
@Result(id = true, property = "id", column = "id"),
@Result(column = "uid", property = "uid"),
@Result(column = "money", property = "money"),

@Result(property = "user", column = "uid",
one = @One(select="com.jwang.dao.IUserDao.findById",
fetchType= FetchType.EAGER))
}) //一对一,立即加载 一个账号对应一个用户
List<Account> findAll();

/**
* 根据用户id查询账号信息
* @param userId
* @return
*/
@Select("select * from account where uid = #{userId}")
List<Account> findAccountByUid(Integer userId);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值