1. 什么是mybatis
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
2. mybatis入门
2.1mybatis下载
mybaits的代码由github.com管理,地址:https://github.com/mybatis/mybatis-3/releases
2.2工程搭建
第一步:创建java工程
第二步:加入需要的jar包
第三步:配置db.properties文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssm?characterEncoding=utf-8 jdbc.username=root jdbc.password=123456
第四步:编写mybatyis配置文件SqlMapConfig.xml
为了看起来清晰,就把所有的配置文件放在一个新的文件夹中,所有在项目下新建一个文件夹config,把他设置为资源目录(classpath,src也是资源目录),然后新建一个SqlMapConfig.xml文件,编写如下配置,不要忘了包扫描:
<?xml version="1.0" encoding="uTF-8" ?>
<!-- mybatis核心配置 -->
<!-- 导入约束的路径 -->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 配置信息 -->
<configuration>
<!-- 引入并加载外部文件 -->
<properties resource="db.properties"></properties>
<!-- 给类取别名 -->
<typeAliases>
<!-- 单独取别名
<typeAlias type="com.entity.Admin" alias="admin"></typeAlias>
-->
<!-- 使用包扫描方式取别名 -->
<package name="com.entity"/>
</typeAliases>
<!-- 环境配置的集合 -->
<environments default="mysql">
<environment id="mysql">
<!-- 事务管理:type指定事务的管理方式,jdbc:交给jdbc管理,MANAGED:被管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据库配置:type是否使用连接池,POOLED:使用连接池,UNPOOLED:不使用连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<!-- 单独加载映射文件
<mapper resource="adminMapper.xml"></mapper>
-->
<!-- 加载整个包 -->
<package name="com.dao"/>
</mappers>
</configuration>
第五步:创建po实体类
ackage com.entity;
public class User {
private int id;
private String username;
private String password;
//get和set,toString方法在此略,使用时需要生成
}
第六步:编写mybatyis映射文件
新建一个userDao.xml文件,这个文件要和下面要创建的接口类放在同一个目录。编写如下配置:
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.UserDao">
<!--查询所有信息
id是这个select的唯一标识
resultType是返回类型
parameterType是参数类型
-->
<select id="findAll" resultType="com.dao.User">
select * from user
</select>
</mapper>
第七步:创建一个接口
package com.dao;
import com.entity.User;
import java.util.List;
public interface UserDao {
List<User> findAll();
}
接口中的方法要和select的id相同,其他雷同,一定不要忘了在接口中写对应的方法。
第八步:编写测试类
在使用系统的测试类时,可以在测试目录下新建一个另类,不能以Test命名,然后写@Test导入junit的类,然后写方法可以直接测试,不需要写主方法:
package test;
import com.entity.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
public class Mybatis02 {
@Test
public void findAll() throws IOException {
//加载核心配置文件
Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
//创建SessionFactory
SqlSessionFactory build= new SqlSessionFactoryBuilder().build(reader);
//创建一个session对象
SqlSession session = build.openSession();
//操作映射
List<User> list = session.selectList("com.dao.UserDao.findAll");
System.out.println(list);
}
}
2.3自定义文件
在这里就自定义一个xml文件,使用起来方便点。

2.4配置日志文件
新建一个log4j.properties文件,配置如下:
### 设置当前日志级别 ###
log4j.rootLogger = debug,stdout,D,E
### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到=E://logs/log.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
配置之后导入log4j的相关jar包,创建日志对象,把信息写到日志文件中,尽量每天一个日志文件,把debug和error的日志写到日志文件中,方便查阅:
//创建日志对象 Logger logger=Logger.getLogger(Mybatis02.class);
会在控制台输出一些日志,这句代码放在类中方法之前,debug信息会自动写到文件中,error信息要在发生异常的地方手动添加。
catch (IOException e) {
//把错误信息写到日志文件
logger.error(e.getMessage());
e.printStackTrace();
}
3. mybatis的增删改查
这里有多个方法,所以进行代码优化:
public SqlSessionFactory build=null;
//这个在方法加载的时候就会初始化
@Before
public void before(){
Logger logger=Logger.getLogger(Mybatis02.class);
Reader reader=null;
//加载核心配置文件
try {
reader= Resources.getResourceAsReader("SqlMapConfig.xml");
//创建SessionFactory
build = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
3.1根据id查询
<!-- 如果参数是简单数据类型,可以省略 -->
<select id="findById" parameterType="int" resultType="User">
select * from user where id=#{id}
</select>
接口中的方法比较简单,这里就不展示,不要忘了编写对应的方法
@Test
public void findById(){
SqlSession session = build.openSession();
User user=session.selectOne("com.dao.UserDao.findById",14);
System.out.println(user);
}
com.dao.UserDao.findById代表的是接口中的方法,后面接参数
3.2查询所有
<select id="findAll" resultType="com.entity.User">
select * from user
</select>
@Test
public void findAll() throws IOException {
//创建一个session对象
SqlSession session = build.openSession();
//操作映射
List<User> list = session.selectList("com.dao.UserDao.findAll");
System.out.println(list);
}
3.3根据用户名和密码查询
<select id="findByNmAndPwd" parameterType="User" resultType="User">
select * from user where username=#{username} and password=#{password}
</select>
#{ }里面的参数一定要和对象中的属性保持一致
@Test
public void findByNmAndPwd(){
SqlSession session = build.openSession();
User user=new User();
user.setUsername("方启豪");
user.setPassword("123");
User user2 = session.selectOne("com.dao.UserDao.findByNmAndPwd", user);
System.out.println(user2);
}
3.4添加用户
<insert id="addUser" parameterType="User">
insert into user values(null,#{username},#{password})
</insert>
增加删除修改一定要提交事务
@Test
public void addUser(){
SqlSession session = build.openSession();
User user=new User();
user.setUsername("方启豪5号");
user.setPassword("12315");
int i = session.insert("com.dao.UserDao.addUser", user);
session.commit();//提交事务
System.out.println(i);
}
3.5修改用户
<update id="updateUser" parameterType="com.entity.User">
update user set username=#{username},password=#{password} where id=#{id}
</update>
@Test
public void updateUser(){
SqlSession session = build.openSession();
User user=new User();
user.setId(9);
user.setUsername("方启豪2号");
user.setPassword("123");
session.insert("com.dao.UserDao.updateUser", user);
session.commit();
}
3.6删除用户
<delete id="deleteUser">
delete from user where id=#{value}
</delete>
@Test
public void deleteUser(){
SqlSession session = build.openSession();
session.delete("com.dao.UserDao.deleteUser", 9);
session.commit();
}
3.7模糊查询:一个条件
使用${},并且括号里面只能写value,这里参数是简单数据类型
<!-- 模糊查询:一个条件,使用${},并且括号里面只能写value -->
<select id="findByLikeOne" resultType="User">
select * from user where username like '%${value}%'
</select>
@Test
public void findByLikeOne(){
SqlSession session = build.openSession();
List<User> list=session.selectList("com.dao.UserDao.findByLikeOne", "启");
System.out.println(list);
}
3.8模糊查询:多个条件
使用${},并且括号里面要与对象的属性相同
<!-- 模糊查询:多个条件,使用${},并且括号里面要与对象的属性相同 -->
<select id="findByLikeMore" parameterType="User" resultType="User">
select * from user where username like '%${username}%' and password like '%${password}%'
</select>
@Test
public void findByLikeMore(){
SqlSession session = build.openSession();
User user=new User();
user.setUsername("钟");
user.setPassword("1");
List<User> list=session.selectList("com.dao.UserDao.findByLikeMore", user);
System.out.println(list);
}
4. 小结
4.1#{}和${}的区别
#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
4.2parameterType和resultType
parameterType:指定输入参数类型
resultType:指定输出结果类型
4.3selectOne和selectList
selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常:
org.apache.ibatis.exceptions.TooManyResultsException
selectList可以查询一条或多条记录
4.4mybatis与hibernate的区别
Mybatis是对jdbc的封装,注重的是sql语句,是一种轻量级的半自动框架,适用于敏捷开发,原理是反射
Hibernate注重映射关系,是一种重量级的全自动的框架。
4.5取别名typeAliases
mybatis支持别名:
|
别名 |
映射的类型 |
|
_byte |
byte |
|
_long |
long |
|
_short |
short |
|
_int |
int |
|
_integer |
int |
|
_double |
double |
|
_float |
float |
|
_boolean |
boolean |
|
string |
String |
|
byte |
Byte |
|
long |
Long |
|
short |
Short |
|
int |
Integer |
|
integer |
Integer |
|
double |
Double |
|
float |
Float |
|
boolean |
Boolean |
|
date |
Date |
|
decimal |
BigDecimal |
|
bigdecimal |
BigDecimal |
|
map |
Map |
自定义别名:
在SqlMapConfig.xml中配置:
<typeAliases> <!-- 单个别名定义 --> <typeAlias alias="user" type="com.entity.User"/> <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) --> <package name="com.entity"/> <package name="其它包"/> </typeAliases>
4.6mappers(映射器)
Mapper配置的几种方法:
第一种:<mapper resource=" " />使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml" />
第二种:<mapper class=" " />使用mapper接口类路径
如:<mapper class="com.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
第三种:<package name=""/>注册指定包下的所有mapper接口
如:<package name="com.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
<!-- 加载映射文件 -->
<mappers>
<!-- 单独加载映射文件
<mapper resource="adminMapper.xml"></mapper>
-->
<!-- 加载整个包 -->
<package name="com.dao"/>
</mappers>
5. dao开发
5.1原始dao开发
原始Dao开发方法需要程序员编写Dao接口和Dao实现类。
1、编写映射文件:
<?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="test">
<!-- 根据id获取用户信息 -->
<select id="findUserById" parameterType="int" resultType="com.hp.entity.User">
select * from user where id = #{id}
</select>
<!-- 根据name模糊查询 -->
<select id="findLikeName" parameterType="java.lang.String" resultType="com.hp.entity.User">
select * from user where username like '%${value}%'
</select>
<!-- 添加用户 -->
<insert id="insertUser" parameterType="com.hp.entity.User">
<!-- 获取生成的主键的值,返回到user对象中 -->
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,password)
values(#{username},#{password})
</insert>
</mapper>
2、编写dao文件:
接口:
public interface IUserDao {
User findUserById(int id);
List<User> findUserLikeName(String name);
void addUser(User user);
}
实现类:
public class UserDaoImpl implements IUserDao{
SqlSessionFactory factory = null;
public UserDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}
@Override
public User findUserById(int id) {
SqlSession session = factory.openSession();
User user = session.selectOne("test.findById", id);
session.close();
return user;
}
@Override
public List<User> findUserLikeName(String name) {
SqlSession session = factory.openSession();
List<User> list = session.selectList("test.findLikeName", name);
session.close();
return list;
}
@Override
public void addUser(User user) {
SqlSession session = factory.openSession();
session.insert("test.insertUser", user);
session.commit();
session.close();
}
}
3、测试:
public class MybatisTest {
SqlSessionFactory factory = null;
@Before
public void createFactory() throws IOException{
InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(is);
}
@Test
public void test7(){
IUserDao dao = new UserDaoImpl(factory);
User user = dao.findUserById(4);
System.out.println(user);
}
@Test
public void test8(){
IUserDao dao = new UserDaoImpl(factory);
List<User> list = dao.findUserLikeName("李");
System.out.println(list);
}
@Test
public void test9(){
IUserDao dao = new UserDaoImpl(factory);
User user = new User();
user.setPassword("2222");
user.setUsername("张三");
dao.addUser(user);
}
5.2Mapper动态代理方式开发Dao
只需要程序员编写Mapper接口,非常适用于mybatis,Mapper接口开发需要遵循以下规范:
1、Mapper.xml文件中的namespace与mapper接口的路径相同。
2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
配置文件及测试在入门里已经进行了,这里就省略了。动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
//mapper动态代理模板@Test
public void testInsert() {
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = new Emp();
emp.setEname("admin");
emp.setJob("admin");
int flag = 0;
try {
flag = empMapper.insertEmp(emp);
}finally {
sqlSession.commit();
sqlSession.close();
}
if(flag == 1) {
System.out.println("自增主键值:"+emp.getEmpno());
}
}
6. lombok的使用
第一步:安装插件

第二步:导入jar包
第三步:在实体类中使用注解即可
删除User类中的方法,只保留属性,添加注解@Setter,@Getter,那么就可以在其他类中使用get和set方法,这样就简化了代码:
package com.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class User {
private int id;
private String username;
private String password;
}
这样注解还不能生效,还要一点配置:

Lombok注解:
|
注解 |
说明 |
|
@Setter |
注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法 |
|
@Getter |
使用方法同上,区别在于生成的是getter方法 |
|
@ToString |
注解在类,添加toString方法 |
|
@EqualsAndHashCode |
注解在类,生成hashCode和equals方法 |
|
@NoArgsConstructor |
注解在类,生成无参的构造方法 |
|
@RequiredArgsConstructor |
注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段 |
|
@AllArgsConstructor |
注解在类,生成包含类中所有字段的构造方法 |
|
@Data |
注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法 |
|
@Slf4j |
注解在类,生成log变量,严格意义来说是常量 |
7.resultMap与parameterMap
7.1复杂条件的查询举例
当参数不属于同一个对象时,使用简单的查询非常麻烦,那么可以把一个对象作为另一个对象的属性结合使用:
ackage com.entity;
public class Person {
private String username;
}
package com.entity;
public class People {
private int age;
}
把上面的两个对象作为属性给PVo,那么他使用起来就比较方便了。
package com.entity;
public class PVo {
private People people;
private Person person;
}
一个简单的插入语句:
<insert id="Pvo" parameterType="PVo">
insert into user valuesnull,#{person.username},#{people.password})
</insert>
7.2resultMap的使用
当查询的结果不仅仅是一个对象的全部属性时,如果再创建一个对象来封装数据就显得格外的麻烦,那么可以使用resultMap来对属性进行重定义,这样就可以直接返回一个map的结果。
复杂查询:查询用户的id,用户名以及所对应的成绩的id和具体成绩(一对一)
数据库信息

<!-- 复杂查询:查询用户的id,用户名以及所对应的成绩的id和具体成绩 -->
<!--定义表数据与组合类的映射关系,id是唯一标识,type是类型-->
<resultMap id="userAndScore" type="User">
<!--id是主键映射,result是非主键映射,column是数据库中的列名,properties是对象的属性名-->
<!--如果多个表中的属性名一样,要注意区分,如这里的id-->
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<!--association对属性是对象的变量的深入映射,适用于一对一的关系-->
<association property="score" javaType="Score">
<id column="sid" property="id"></id>
<result column="grade" property="grade"></result>
<result column="u_id" property="u_id"></result>
</association>
</resultMap>
<!--这里的返回类型必须是map集合-->
<select id="findUserAndScore" resultMap="userAndScore">
select user.id uid, username,score.id sid,grade from user,score where user.id=score.u_id
</select>
User类
package com.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class User {
private int id;
private String username;
private String password;
//score的类型是对象
private Score score;
}
Score类
package com.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class Score {
private int id;
private double grade;
private int u_id;
}
测试方法
@Test
public void findUserAndScore(){
SqlSession session = build.openSession();
List<User> list=session.selectList("com.dao.UserDao.findUserAndScore");
System.out.println(list);
}
复杂查询:查询用户的id,用户名以及所对应的成绩的id和具体成绩(一对多)
数据库信息

<resultMap id="userAndScore" type="User">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<!--collection对属性是对象的变量的深入映射,适用于一对多的关系-->
<!--javaType指属性是什么类型,ofType指集合中装的什么类型-->
<collection property="scores" javaType="java.util.List" ofType="Score">
<id column="sid" property="id"></id>
<result column="grade" property="grade"></result>
<result column="u_id" property="u_id"></result>
</collection>
</resultMap>
<!--这里的返回类型必须是map集合-->
<select id="findUserAndScore" resultMap="userAndScore">
select user.id uid, username,score.id sid,grade from user,score where user.id=score.u_id
</select>
User类如下,score和测试方法没有变。

8.动态sql
8.1if标签
当标签里面test的内容为true时才会去执行if里面的语句
查询:根据id查询,如果id不为空就根据id查询,否则就让id为0进行查询
<select id="findById" resultType="User">
select * from user
<if test="value!=null">
where id=#{value}
</if>
<if test="value==null">
where id=0
</if>
</select>
8.2where标签
where会自动去识别,当sql语句中已经有where那么它会直接拼接where里面的内容,如果没有就直接使用;另外where里面的and也类似,如果已经有where就使用and,如果没有就舍弃第一个and。
查询:当id不为空就根据id查询,否则就查询所有
<select id="findById" resultType="User">
select * from user where 1=1
<if test="value!=null">
<where>
and id=#{value}
</where>
</if>
</select>
8.3foreach标签
用来遍历集合等,用于传入参数是多个相同类型的查询,可以使用在in,limit,between。。。
查询:传入多个id查询用户信息
<select id="findByIn" parameterType="java.util.List" resultType="User">
select * from user
<!--如果传入参数是list类型,必须使用list获取,并且使用and-->
<if test="list.size()>0 and list!=null">
where id in
<!--collection要遍历的集合,open开始的符号,close结束的符号,separator分隔符-->
<!--index是索引,item是每一条内容,这里是list.get(i)-->
<foreach collection="list" open="(" separator="," close=")" index="i" item="item" >
#{item}
</foreach>
</if>
</select>
@Test
public void findByIn(){
SqlSession session = build.openSession();
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(14);
list.add(22);
List<User> list1=session.selectList("com.dao.UserDao.findByIn",list);
System.out.println(list1);
}
8.4sql片段
将会重复使用的sql语句抽取出来,需要使用的地方直接引入
<!--sql片段-->
<sql id="select">
select * from user
</sql>
<select id="findById" resultType="User">
<!--引入sql片段-->
<include refid="select"/>
where is=#{value}
</select>
9.延迟加载
需要查询关联信息时,我们先查询一部分信息,再关联查询关联信息,叫延迟加载。主要通过resultMap实现延迟加载。延迟加载需要导入cglib以及asm的jar包。
9.1打开延时加载的开关
在SqlMapConfig.xml中配置:
<settings>
<!-- 延迟加载开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 当设置为true的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
9.2写sql语句
根据用户名和密码查询出id,然后再根据id查询对应的成绩

User类和Score类在此略,可参考上面7.2复杂查询中一对多查询的定义
9.3测试
@Test
public void findUserAndScore2(){
SqlSession session = build.openSession();
User user=new User();
user.setUsername("钟玉石");
user.setPassword("123");
List<User> list=session.selectList("com.dao.UserDao.findUserAndScore2",user);
System.out.println(list.get(0).getId());
System.out.println("-------------");
System.out.println(list.get(0).getScores().get(0));
}
查询之后可以看到先执行一次查询,然后再去执行后面的查询,并没有一次性查询出结果。
9. Mybatis缓存
将从数据库查询的数据存储到内存中缓存起来,这样就不用从数据库中查询数据而从缓存中查询,提高查询的速度,减少对数据库的访问。
10.1一级缓存
一级缓存使用
一级缓存:当执行两条相同的查询语句时,mybatis只执行一次查询,map中存储了sql执行查询的结果集。
一级缓存是session级别的缓存,当执行增删改中的任何一个操作,缓存就会清空。
一级缓存查询:
@Test
public void findById(){
SqlSession session = build.openSession();
User user=session.selectOne("com.dao.UserDao.findById",14);
System.out.println(user);
System.out.println("----------");
User user2=session.selectOne("com.dao.UserDao.findById",14);
System.out.println(user2);
}
查询结果如下

一级缓存查询中执行增加操作:
@Test
public void findById(){
SqlSession session = build.openSession();
User user=session.selectOne("com.dao.UserDao.findById",14);
System.out.println(user);
User user3=new User();
user3.setUsername("方启豪6号");
user3.setPassword("12315");
session.insert("com.dao.UserDao.addUser", user3);
session.commit();//提交事务
System.out.println("----------");
User user2=session.selectOne("com.dao.UserDao.findById",14);
System.out.println(user2);
}
只要清空了session,那么就清除了缓存,就需要重新查询。也可以手动清空session,语句是

一级缓存原理

第一次查询先去缓存中找是否有缓存数据,发现没有,查询数据库,将查询到的数据写入sqlsession的一级缓存区域。
第二次查询先去缓存中找是否有缓存数据,发现有,直接从缓存区域中取出数据返回。如果执行sqlsession的添加、修改、删除等操作,会执行commit,最终会清空缓存。
10.2二级缓存
二级缓存是mapper(命名空间)级别的缓存,默认是开启的,只有session提交或关闭时才能把结果提交到二级缓存中,当执行增删改中的任何一个操作,缓存就会清空。
二级缓存的使用
二级缓存的使用需要进行配置:
第一步:在SqlMapConfig.xml的settings中加一行代码:
<!--开启并标识二级缓存--> <setting name="cacheEnabled" value="true"/>
第二步:在userDao.xml中加一行代码:

第三步:User类要实现Serializable接口

二级缓存测试
@Test
public void findById(){
SqlSession session = build.openSession();
SqlSession session2 = build.openSession();
UserDao mapper=session.getMapper(UserDao.class);
UserDao mapper2=session2.getMapper(UserDao.class);
User user=mapper.findById(14);
System.out.println(user);
session.close();
System.out.println("----------");
User user2=mapper2.findById(14);
System.out.println(user2);
session2.close();
}
查询结果

二级缓存原理

不同的sqlsession都要调用mapper下的sql语句发起数据库请求。sqlsession1执行UserMapper下的查询用户请求先从二级缓存中查找有没有数据,如果没有就从数据库中查询,并且将查询到数据存储二级缓存中。
sqlsession2执行UserMapper下的同一个查询用户请求,先从二级缓存中查找有没有数据,如果有就从二级缓存中查询数据,返回。
如果有一个sqlsession3执行UserMapper下添加、修改、删除语句,执行commit操作后,将UserMapper下的所有缓存数据全部清空。
9. Mybatis注解
11.1@Param参数传入
当参数少于4个的时候可以使用注解参数的传入,如果参数过多,就使用原始pojo的方式
UserDao类:
//使用注解Param传递参数,引号里面的参数是要使用的参数,如#{username}
User findByNmAndPwd(@Param("username")String username,@Param("password")String password);
userDao.xml中sql:
<select id="findByNmAndPwd" resultType="User">
select * from user where username=#{username} and password=#{password}
</select>
测试类:
@Test
public void findByNmAndPwd(){
SqlSession session = build.openSession();
UserDao mapper = session.getMapper(UserDao.class);
User user=mapper.findByNmAndPwd("方启豪","123");
System.out.println(user);
}
11.2@Insert @Update @Delete
此注解不需要userDao.xml文件,这三个用法雷同,以update为例:
UserDao类:
//这里参数传的是一个对象
@Update("update user set username=#{user.username}, password=#{user.password} where id=#{user.id}")
void updateUser(@Param("user")User user);
测试类:
@Test
public void updateUser(){
SqlSession session = build.openSession();
UserDao mapper = session.getMapper(UserDao.class);
User user=new User();
user.setId(14);
user.setUsername("方启豪222号");
user.setPassword("123");
mapper.updateUser(user);
session.commit();
}
11.3@Select
此注解不需要userDao.xml文件
UserDao类:
@Select("select * from user where id=#{id}")
User findById(@Param("id") Integer id);
测试类:
Test
public void findById(){
SqlSession session = build.openSession();
UserDao mapper=session.getMapper(UserDao.class);
User user=mapper.findById(14);
System.out.println(user);
}