Mybatis

Posted zys2019

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis相关的知识,希望对你有一定的参考价值。

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,把他设置为资源目录(classpathsrc也是资源目录),然后新建一个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();
  

接口中的方法要和selectid相同,其他雷同,一定不要忘了在接口中写对应的方法。

第八步:编写测试类

在使用系统的测试类时,可以在测试目录下新建一个另类,不能以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] %dyyyy-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 = %-dyyyy-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 = %-dyyyy-MM-dd HH:mm:ss  [ %t:%r ] - [ %p ]  %m%n

配置之后导入log4j的相关jar包,创建日志对象,把信息写到日志文件中,尽量每天一个日志文件,把debugerror的日志写到日志文件中,方便查阅:

 

//创建日志对象
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.2parameterTyperesultType

parameterType:指定输入参数类型

resultType:指定输出结果类型

4.3selectOneselectList

selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常:

org.apache.ibatis.exceptions.TooManyResultsException

selectList可以查询一条或多条记录

4.4mybatishibernate的区别

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接口,非常适用于mybatisMapper接口开发需要遵循以下规范:

1Mapper.xml文件中的namespacemapper接口的路径相同。

2Mapper接口方法名和Mapper.xml中定义的每个statementid相同

3Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql parameterType的类型相同

4Mapper接口方法的输出参数类型和mapper.xml中定义的每个sqlresultType的类型相同

配置文件及测试在入门里已经进行了,这里就省略了。动态代理对象调用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,那么就可以在其他类中使用getset方法,这样就简化了代码:

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

注解在类,生成hashCodeequals方法

@NoArgsConstructor

注解在类,生成无参的构造方法

@RequiredArgsConstructor

注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段

@AllArgsConstructor

注解在类,生成包含类中所有字段的构造方法

@Data

注解在类,生成setter/getterequalscanEqualhashCodetoString方法,如为final属性,则不会为该属性生成setter方法

@Slf4j

注解在类,生成log变量,严格意义来说是常量

 

7.resultMapparameterMap

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查询,否则就让id0进行查询

<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以及asmjar包。

9.1打开延时加载的开关

SqlMapConfig.xml中配置:

<settings>
    <!-- 延迟加载开关 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 当设置为true的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

9.2sql语句

根据用户名和密码查询出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.xmlsettings中加一行代码:

<!--开启并标识二级缓存-->
<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.xmlsql:

<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);

 

以上是关于Mybatis的主要内容,如果未能解决你的问题,请参考以下文章

MybatisMyBatis 注解方式的基本 用法

MybatisMyBatis之缓存

MyBatisMyBatis的增删改查

Mybatismybatis登录实例

MybatisMybatis基础(中)

MybatisMybatis缓存