关于java:Mybatis全面详解

一、什么是Mybatis

这里借用官网的一句话介绍什么是mybatis:MyBatis 是一款优良的长久层框架,它反对定制化 SQL、存储过程以及高级映射。MyBatis 防止了简直所有的 JDBC 代码和手动设置参数以及获取后果集。MyBatis 能够应用简略的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,一般的 Java对象)映射成数据库中的记录。
二、Mybatis绝对JDBC有哪些劣势

首先咱们来看一看jdbc连贯数据库的连贯办法:

     public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        
        try {
            //1、加载数据库驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2、通过驱动治理类获取数据库链接
            connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "root");
            //3、定义sql语句 ?示意占位符
                String sql = "select * from user where username = ?";
            //4、获取预处理statement
            preparedStatement = connection.prepareStatement(sql);
            //5、设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
            preparedStatement.setString(1, "王五");
            //6、向数据库收回sql执行查问,查问出后果集
            resultSet =  preparedStatement.executeQuery();
            //7、遍历查问后果集
            while(resultSet.next()){
                User user 
                System.out.println(resultSet.getString("id")+"  "+resultSet.getString("username"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            //8、开释资源
            if(resultSet!=null){
                try {
                    resultSet.close();//开释后果集
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(preparedStatement!=null){
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(connection!=null){
                try {
                    connection.close();//敞开数据库连贯
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }

    }

通过下面的一段jdbc连贯数据代码,咱们看有哪些不好的中央:

1.在创立connection的时候,存在硬编码问题(也就是间接把连贯信息写死,不不便前期保护)

2.preparedStatement对象在执行sql语句的时候存在硬编码问题。

3.每次在进行一次数据库连贯后都会敞开数据库连贯,频繁的开启/敞开数据连贯影响性能。

简略的说一下mybatis绝对jdbc的劣势:

1.mybatis是把连贯数据库的信息都是写在配置文件中,因而不存在硬编码问题,不便前期保护。

2.mybatis执行的sql语句都是通过配置文件进行配置,不须要写在java代码中。

3.mybatis的连接池治理、缓存治理等让连贯数据库和查问数据效率更高。
三、Mybatis框架的原理介绍

这里就通过一张图来对mybatis框架原理进行介绍吧:

四、Mybatis全局配置文件

SqlMapConfig.xml是Mybatis的全局配置文件,它的名称能够是任意,然而个别命名都为(SqlMapConfig)

4.1.全局配置文件的类容和程序

Properties(属性)

Settings(全局参数设置)

typeAliases(类型别名)

typeHandlers(类型处理器)

objectFactory(对象工厂)

plugins(插件)

environments(环境信息汇合)

environment(单个环境信息)

transactionManager(事物)

dataSource(数据源)

mappers(映射器)

4.2.常见配置详解

properties标签:

Mybatis能够通过该标签来读取java配置信息:

例如在工程中对数据源信息写在db.properties文件中,能够通过properties标签来加载该文件。

db.properties:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/mybatis
db.username=root
db.password=12345678

SqlMapConfig.xml应用properties标签:

<!-- 通过properties标签,读取java配置文件的内容 -->
<properties resource="db.properties" /> 
<!-- 配置mybatis的环境信息 -->
<environments default="development">
<environment id="development">
    <!-- 配置JDBC事务管制,由mybatis进行治理 -->
    <transactionManager type="JDBC"></transactionManager>
    <!-- 配置数据源,采纳dbcp连接池 -->
    <dataSource type="POOLED">
        <property name="driver" value="${db.driver}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
    </dataSource>
</environment>
</environments>

留神:

1、 先加载properties中property标签申明的属性

因而在property中的name属性的值和value比properties中的resource属性先加载。后加载的db.properties会笼罩于property加载属性和值。

<properties resource="db.properties">

    <property name="db.username",value="1234"/>

</properties>   

2、 再加载properties标签引入的java配置文件中的属性

3、 parameterType的值会和properties的属性值发生冲突。因而,在properties文件里的内容命名最好加上db.代表是跟数据源相干的属性,这样就不容易跟当前的属性发生冲突。

settings标签:
该标签时mybatis的全局设置,该设置会影响mybatis的运行。

个别咱们应用应用该标签来开启二级缓存和懒加载。

以下是几张settings配置项的阐明:

typeAliases标签

该标签是对po类进行别名设置,这样,在前面应用po类的时候就能够间接通过别名援用,而不须要通过po类的全限定名来援用。这样能够进步咱们的开发效率。

首先介绍下Mybatis的默认提供的别名有:


自定义单个别名:这种形式只能定义单个类的别名。

上面的代码就是把com.lc.mybatis.po.User类定义为user的别名

 <typeAliases>
      <!-- 设置单个别名 -->
      <typeAlias type="com.lc.mybatis.po.User" alias="user"/>
 </typeAliases>

自定义之批量定义别名:

上面代码是把com.lc.mybatis.po类下的所有类都申明别名,默认的别名就是类名(类名大小写都能够)

 <!-- 设置别名 -->
      <typeAliases>
           <!-- 批量设置别名 -->
           <!-- package:指定包名称来为该包下的po类申明别名,默认的别名就是类名(类名首字母大小写都能够) -->
           <package name="com.lc.mybatis.po"/>
      </typeAliases>

mappers标签

该标签的作用是加载映射文件

形式一:<mapper resource=””/>

该形式是加载绝对于类门路下的映射文件:

 <mappers>
   <mapper resource="sqlmap/User.xml"/>
 </mappers>

形式二:<mapper url=””/>

该形式应用全限定门路

   <mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />

形式三:<mapper class=””/>

该形式应用mapper接口的全限定类名

   <mapper class="cn.itcast.lc.mapper.UserMapper"/>

此形式要求:

Mapper接口Mapper映射文件名称雷同且在同一个目录下。

形式四:<package name=””/>

该形式是加载指定包下的所有映射文件
<package name="cn.lc.mybatis.mapper"/>

此形式要求:

Mapper接口Mapper映射文件名称雷同且在同一个目录下。

五、映射文件

5.1.输出映射parameterType

第一种:简略类型

{}示意占位符?,parameterType接管简略类型的参数时,外面的名称能够任意

<select id="findUserById" parameterType="java.lang.Integer" resultType="user">
        SELECT * FROM USER WHERE id = #{id}
</select>

第二种:pojo类型

这里通过用户的用户名进行含糊查问演示pojo类型

在映射文件中增加含糊查问语句:
<!– parameterType传递pojo类型 –>

<select id="findUsersByPojo" parameterType="com.lc.mybatis.po.User" resultType="com.lc.mybatis.po.User">
    SELECT  * FROM USER WHERE username LIKE "%${username}%"
</select>

user类

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

测试类:

    public class UserDao{
    //依据用户名进行含糊查问
    @Override
        public List<User> findUserByPojo(){ 
            //全局配置文件门路:
             String resource = "SqlMapConfig.xml";
             InputStream inputStream = Resources.getResourceAsStream(resource);
             //创立SqlSessionFactory
             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
             SqlSession sqlSession = sqlSessionFactory.openSession();
             User user = new User();
             user.setUsetname("张三");
             List<User> users = (List<User>)sqlSession.selectList("test.findUsersByPojo",user);//传入pojo
             System.out.println(users);
             sqlSession.close();
                     return users;
        }
    }

第三种:包装类型pojo

这里通过用户名和用户地址对用户进行查问来演示包装类型pojo:

首先创立包装pojo类:

public class UserVO {
    private User user;

    public User getUser() {
    return user;
    }

    public void setUser(User user) {
    this.user = user;
}

}

在映射文件中增加查问语句

    <!-- parameterType传递pojo包装类型 -->
    <select id="findUsersByPojo1" parameterType="com.lc.mybatis.po.UserVO" resultType="user"> 
        SELECT * FROM USER WHERE username LIKE "%${user.username}%" AND address=#{user.address}
    </select>

测试类

public class UserDao{
//依据用户名和地址进行查问
    @Override
    public List<User> findUserByPojo1(){
    //全局配置文件门路:
    String resource = "SqlMapConfig.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
     //创立SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    User user = new User();
    user.setUsetname("张三");
    user.setAddress("郝汉山");
    UserVO userVo = new UserVO();
    userVo.setUser(user);
    List<User> users = (List<User>)sqlSession.selectList("test.findUsersByPojo1",userVo);//传入pojo包装类
    System.out.println(users);
    return users;
    }

 }

第四种:map汇合类型

这里通过查问用户信息演示:

在映射文件中增加该查问语句

<!-- parameterType传递hashmap类型 -->
    <select id="findUsersByMap" parameterType="java.util.Map" resultType="user">
    SELECT * FROM USER WHERE username LIKE "%${username}%" AND address=#{address}
    </select>

测试方法:

public class UserDao{
//依据用户名和地址进行查问
@Override
public List<User> findUserByMap(){
    //全局配置文件门路:
    String resource = "SqlMapConfig.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //创立SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    Map<String,String> map = new HashMap<>();
    map.put("username","张三");
    map.put("address","郝汉山");
    List<User> users = (List<User>) sqlSession.selectList("test.findUsersByMap",map);//传入pojo包装类
    System.out.println(users);
    sqlSession.close();
    return users;
    }
    }

5.2.resultType后果映射

resultType后果映射要求:须要查问后果的列名和映射的对象的属性名统一,这样能力映射胜利。如果映射没胜利也不会报错,只是映射后果中对象的相应属性没有值,为空。如果映射的列名和对象中的属性名全副不统一,那么映射的对象为空。如果在应用sql语句查问的时候给查问后果列设置了别名,则别名要和映射后果对象的属性名统一,这样能力保障映射胜利。

第一种:简略类型

留神: 如果后果映射为简略类型,则须要查问的后果为一列能力映射胜利。

例如:查问用户表中用户的总数。

映射文件为:

<!-- resultType:输入为简略类型 -->
    <select id="findUserCount" resultType="int">
    select count(*) from user;
    </select>

测试代码:

public class UserDao{
    @Override
    public int findUserCount(){
    //全局配置文件门路:
    String resource = "SqlMapConfig.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //创立SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    int userCount= sqlSession.selectOne("test.findUserCount");
    System.out.println(userCount);
    sqlSession.close();
    return userCount;
    }

    }

第二种:pojo后果映射

这里操作pojo输出映射。

5.3.resultMap后果映射

应用resultMap后果映射时,不须要查问进去的后果集的列名和映射后果对象的属性名雷同,然而须要申明一个resultMap,手动的形式来对列名和对象属性进行映射。(resultMap个别用于多表关联映射)

例如:通过查问用户表中的用户,并对查问进去的用户表的列名设置别名。

映射文件增加查问语句:

[id]:定义resultMap的惟一标识

[type]:定义该resultMap最终映射的pojo对象

[id标签]:映射后果集的惟一标识列,如果是多个字段联结惟一,则定义多个id标签

[result标签]:映射后果集的一般列

[column]:SQL查问的列名,如果列有别名,则该处填写别名

[property]:pojo对象的属性名

<!-- 如果查问进去的列名有别名就不能通过resultType来接管输入类型了。须要通过resultMap来申明传出类型(resultMap须要申明) -->
<resultMap type="user" id="userMap">
    <!-- id标签:专门查问后果中惟一列映射 -->
    <id column="id_" property="id"/>
    <!-- result标签:映射查问后果中的一般列 -->
    <result column="username_" property="username"/>
    <result column="address_" property="address"/>
</resultMap>
<select id="findUserResultMap" parameterType="int" resultMap="userMap">
    select id id_,username username_,address address_ from user where id=#{id}
</select>

测试类:

public class UserDao{
    @Override
    public User findUserResultMap(){
            //全局配置文件门路:
            String resource = "SqlMapConfig.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //创立SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            User user = sqlSession.selectOne("test.findUserResultMap",1);
            System.out.println(user);
            sqlSession.close();
            return user;
    }

}

5.4.动静sql

在mybatis中提供了一些动静sql标签,能够让咱们开发效率更快,这些动静sql语句能够减少咱们写的sql语句的重用性,罕用的动静sql语句标签有:if标签、sql片段(须要先定义后应用)、where标签、foreach标签

通过上面例子来演示动静sql罕用的标签:

需要剖析:查问用户表中相干的用户。

收回相干的sq语句有:

select * from user where username like "%张三%" and address= ?;

      select * from user;

      select * from user where id in(1,2,10);

如何能力让这些语句在咱们须要的时候就应用,不须要的时候就不实用。其实咱们发现这三条语句有很多反复的中央,咱们如果能做到让反复的可能重复使用,没有反复的能够按咱们需要应用的话就能缩小咱们写很多sql语句。当然动静sql就是帮咱们解决这些问题的。

映射文件:

<!-- 定义sql片段,用于可重用的sql片段 -->
<sql id="whereClause">
    <!-- if标签:能够对输出的参数进行判断 -->
    <!-- test:指定判断表达式 -->
    <if test="user!=null">
        <if test="user.username!=null and user.username!=''">
            AND username LIKE '%${user.username}%'
        </if>
        <if test="user.address!=null and user.address!=''">
            AND address=#{user.address}
        </if>
    </if>
    <if test="idList!=null">
        AND id IN
        <!-- foreach标签: 能够循环传入参数值 -->
        <!-- collenction:标示pojo中汇合属性的属性名称 -->
        <!-- item:每次遍历进去的对象 -->
                    <!--open开始遍历时拼接的串-->
                    <!--close完结遍历时拼接的串-->
                    <!--separator遍历每个对象之间须要拼接字符-->
                    <foreach collection="idList" item="item" open="(" close=")" separator=",">
            #{item}
        </foreach>
    </if>
</sql>

<select id="findUserList" parameterType="userVO" resultType="user">
    SELECT * FROM USER
    <!-- where标签: 默认去掉第一个AND,如果没有参数就去掉where本人-->
            <where>
                <!--援用sql语句片段-->
                 <include refid="whereClause"></include>
    </where>
</select>

userVo类:

public class UserVO {
private User user;
private List<Integer> idList;


public List<Integer> getIdList() {
    return idList;
}

public void setIdList(List<Integer> idList) {
    this.idList = idList;
}

public User getUser() {
    return user;
}

public void setUser(User user) {
    this.user = user;
}

}

测试类:

public class UserDao{
    @Override
    public void findUserList(){
            //全局配置文件门路:
            String resource = "SqlMapConfig.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //创立SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //测试foreach语句执行
            UserVO userVo = new UserVO();
            List<Integer> idList = new ArrayList<>();
            idList.add(1);
            idList.add(2);
            dList.add(3);
            userVo.setIdList(idList);
            //测试select * from user where username like ? and address=?;
            //User user = new User();
            //user.setUsername("张三");
            //user.setAddress("武当山");
            //userVo.setUser(user);
            List<User> users = sqlSession.selectOne("test.findUserList",userVo);
            System.out.println(users);
            sqlSession.close(); 
    }

}

六、Mybatis之传统Dao层的开发(该形式很少用)

6.1.开发环境筹备:

        1.集体应用的jdk为1.7

        2.开发工具是应用的Eclipse4.5

        3.数据库应用的MySQL5X

        4.导入mybatis所须要的jar包

5.导入mysql数据库驱动包       
6.2.需要剖析:

依据ID查问查问用户、依据用户名称进行含糊查问、增加用户

6.3.代码实现

创立用户表对应pojo类User:

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    //setter和get办法省略
}

创立mybatis的全局配置文件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>
<!-- 配置mybatis的环境信息 -->
<environments default="developments">
    <environment id="developments">
        <!-- 配置JDBC事务管制,由mybatis进行治理 -->
        <transactionManager type="JDBC"></transactionManager>
        <!-- 配置数据源,采纳mybatis连接池 -->
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
            <property name="username" value="root"/>
            <property name="password" value="12345678"/>
        </dataSource>
    </environment>
</environments>

<!-- 加载映射文件 -->
<mappers>
    <mapper resource="User.xml"/>
</mappers>
</configuration>

创立需要开发的映射文件User.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">
<!-- 
namespace:命名空间,它的作用就是对SQL进行分类化治理,能够了解为SQL隔离
留神:应用mapper代理开发时,namespace有非凡且重要的作用
     -->
    <mapper namespace="test">
<!-- 依据用户ID查问用户信息 -->
<!-- select:示意一个MappedStatement对象 -->
<!-- id:statement的惟一标识 -->
<!--  #{}:示意一个占位符?-->
<!-- #{id}:外面的id示意输出参数的参数名称,如果该参数为简略类型,那么#{}外面的参数能够任意 -->
<!-- parameterType:输出参数的java类型 -->
<!-- resultType:输入后果的所映射的java类型(单条后果所对应的java类型) -->
<select id="findUserById" parameterType="java.lang.Integer" resultType="com.lc.mybatis.po.User">
    SELECT * FROM USER WHERE id = #{id}
</select>
<!-- 依据用户名进行含糊查问 -->
<!-- ${}:示意一个sql连接符 -->
<!-- ${value}:外面的 value示意输出的名称,如果该参数是简略类型,那么${}外面的参数名称必须是value-->
<!-- ${}这种写法存在sql注入的危险,所以须要 慎用!然而有些场景须要用到比方:排序,-->
<select id="findUsersByName" parameterType="java.lang.String" resultType="com.lc.mybatis.po.User">
    SELECT * FROM USER WHERE username LIKE "%${value}%"
</select>

<!-- 增加用户: -->
<!-- 
    [selectKey标签]:通过select查问来生成主键
    [keyProperty]:指定寄存生成主键的属性
    [resultType]:生成主键所对应的Java类型
    [order]:指定该查问主键SQL语句的执行程序,绝对于insert语句
    [last_insert_id]:MySQL的函数,要配合insert语句一起应用,该函数在mysql中是执行在insert语句后。
-->
<insert id="insertUser" parameterType="com.lc.mybatis.po.User">
    <selectKey keyProperty="id" resultType="int" order="AFTER">
        SELECT LAST_INSERT_ID()
    </selectKey>

    INSERT INTO USER(username,sex,birthday,address) VALUES(#{username},#{sex},#{birthday},#{address})
</insert>
</mapper>

dao层接口:

public interface UserDao {

//依据用户id查问用户
public User findUserById(int id);

    //依据用户名进行含糊查问
    public List<User> findUsersByName(String name);

    //增加用户
    public void addUser(User user);

 }
dao层的实现:

public class UserDaoImpl implements UserDao{
    //通过构造方法注入sqlSessionFactory
private SqlSessionFactory sqlSessionFactory;

public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
}

    //依据id查问用户
@Override
public User findUserById(int id) {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    User user = sqlSession.selectOne("test.findUserById", id);
    System.out.println(user);
    sqlSession.close();
    return user;
}
    //依据用户名进行含糊查问
    @Override
    public List<User> findUsersByName(String name){
            SqlSession sqlSession = sqlSessionFactory.openSession();
            List<User> users = sqlSession.selectList("test.findUsersByName",name);
            System.out.println(users);
            sqlSession.close();
            return users;
    }
    

 //增加用户


public List<User> addUser(String name){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User();
        user.setUsername("超哥2");
        user.setAddress("成都市");
        //调用SqlSession的增删改查办法
        //第一个参数:示意statement的惟一示意
        //第一个参数:示意占位符的
        sqlSession.insert("test.insertUser", user);
        System.out.println(user.getId());
        //切记:增删改操作须要提交事务。
        sqlSession.commit();
        //敞开资源
        sqlSession.close();
}

测试类:这里就只通过依据id查找用户进行测试

public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}

@Test
public void testFindUserById() {
    UserDao userDao = new UserDaoImpl(sqlSessionFactory);
    userDao.findUserById(1);
}

}
七、Mybatis之Mapper接口的开发方式

该形式开发,不须要写dao层的实现类,而是mybatis依据映射文件等信息对接口进行jdk动静代理生成代理类来实现接口中的办法,因而,采纳这种形式,咱们只须要编辑接口,而不须要去写实现。

7.1.需要剖析

依据id查问用户。

7.2.Mapper开发代理标准

1、mapper接口的全限定名要和mapper映射文件的namespace值统一。
2、mapper接口的办法名称要和mapper映射文件的statement的id统一。
3、mapper接口的办法参数类型要和mapper映射文件的statement的parameterType的值统一,而且它的参数是一个。
4、mapper接口的办法返回值类型要和mapper映射文件的statement的resultType的值统一。

7.3.代码实现

筹备po类:

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    //getter和setter办法省略
}

Mapper接口:

 public interface UserMapper {

     public User findUserById(int id);
}

UserMapper.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">
<!-- 
namespace:命名空间,它的作用就是对SQL进行分类化治理,能够了解为SQL隔离
留神:应用mapper代理开发时,namespace有非凡且重要的作用
 -->
<mapper namespace="com.lc.mybatis.mapper.UserMapper">
<!-- 依据用户ID查问用户信息 -->
<!-- select:示意一个MappedStatement对象 -->
<!-- id:statement的惟一标识 -->
<!--  #{}:示意一个占位符?-->
<!-- #{id}:外面的id示意输出参数的参数名称,如果该参数为简略类型,那么#{}外面的参数能够任意 -->
<!-- parameterType:输出参数的java类型 -->
<!-- resultType:输入后果的所映射的java类型(单条后果所对应的java类型) -->
<select id="findUserById" parameterType="java.lang.Integer" resultType="com.lc.mybatis.po.User">
    SELECT * FROM USER WHERE id = #{id}
</select>

</mapper>

在全局配置文件SqlMapperConfig中增加该映射文件

测试代码:

public class UserMapperTest {

private SqlSessionFactory sqlSessionFactory ;

@Before
public void setUp() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //获取UserMapper的代理类
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = userMapper.findUserById(10);
    System.out.println(user);
    sqlSession.close();
}

}

八、Mybatis一级缓存

mybatis提供查问缓存,如果缓存中有数据,就不必从数据库中获取,用于加重数据压力,进步零碎性能

一级缓存是sqlSession级别的缓存,在操作数据库的时候,须要结构sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的SqlSession的缓存区域(HashMap)是相互不受影响的。

mybatis默认是反对一级缓存的。

8.1.证实一级缓存的存在:

验证办法:

 public void testOneLevelCache() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //获取UserMapper的代理类
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //第一次查问
    User user = userMapper.findUserById(10);
    System.out.println(user);
    //第二次查问
    User user2 = userMapper.findUserById(10);
    System.out.println(user2);
    
    sqlSes

通过执行该办法,查看打印日志能够看出就执行了一次sql语句的查问,因而能够判断第二次查问是没有从数据库中进行查问的。

那当什么时候一级缓存会被清空呢?

通过测试发现,当在第二次查询数据库之前对数据库进行了写的操作后,第二次查问就不会从缓存中查问后果,而是间接从数据中查问后果。另一种就是在第一查问后果后,手动的形式清空一级缓存(应用sqlSession.clearCache();办法。)

测试方法:     

九、Mybatis二级缓存

二级缓存是Mapper级别的缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession能够共用二级缓存,二级缓存是夸SqlSession的。(二级缓存Mybatis默认是敞开的,须要本人去手动配置开启或能够本人抉择用哪个厂家的缓存来作为二级缓存)

9.1.开启二级缓存

首先在全局配置文件中配置开启二级缓存性能:       

<!-- 开启二级缓存总开关 -->
<setting name="cacheEnabled" value="true"/>

在映射文件中去抉择是否该映射文件应用二级缓存:

如果应用就进行以下配置,如果不是用,就不须要对映射文件做任何批改。

<!-- 开启本mapper下的namespace的二级缓存,默认应用的是mybatis提供的PerpetualCache -->
<cache></cache> 

也能够设置抉择二级缓存提工商

以下是我抉择应用EhcacheCache缓存(留神:在应用EhcacheCache缓存须要导入jar包)


最初就是,在须要缓存的po类中去实现序列化:

验证二级缓存是否开启:

验证办法如下:

@Test
//测试二级缓存是否开启
public void testTwoLevelCache() {
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    SqlSession sqlSession3 = sqlSessionFactory.openSession();
    //获取UserMapper的代理类
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
    
    User user1 = userMapper1.findUserById(10);
    System.out.println(user1);
    //当sqlSession1执行close()的时候,才把sqlSession1查问的后果写到二级缓存中去。
    sqlSession1.close();
    
//        User u = new User();
//        u.setUsername("小胖");
//        u.setAddress("成都");
//        u.setSex("2");
//        u.setBirthday(new Date());
//        userMapper3.insertUser(u);
//        //当其中任何一个sqlSession执行commit()办法后将刷新整个缓存区
//        sqlSession3.commit();
//        sqlSession3.close();
    
    //第二次查问
    User user2 = userMapper2.findUserById(10);
    System.out.println(user2);
    sqlSession2.close();
    
    
}

测试后果:

同时通过测试,当在第二次查问的时候,向数据库提交数据后,就会对缓存进行刷新,这样在第二次查问的时候就没有走缓存,而是走的数据库。

9.2.禁用二级缓存

因为二级缓存默认是敞开的,如果不开启就不会应用二级缓存。如果,咱们开启了二级缓存,而在有些查问后果集中,不须要受到二级缓存影响,该怎么去做呢?

在select查问中,默认是应用了二级缓存,如果不想应用二级缓存,就在select标签中有一个useCache的属性设置为false,就代表不应用二级缓存,每次进行查问数据都不会从缓存总获取,而是间接从数据库中进行查问。useCache的默认值是true,即代表statement应用二级缓存。

9.3.刷新二级缓存

在statement中设置flushCache=true,能够刷新二级缓存。默认状况下,select语句中的flushCache是false。如果是insert、update、delete语句,那么flushCache的默认值是true。如果将select语句中的flushCache值改为true,就意味着查问语句的二级缓存生效,每次查问都会从数据库进行查问。如果将select语句的flushCache值为false,就代表该查问语句应用了二级缓存,如果在数据库中批改了数据,而二级缓存中的数据还是原来的数据,那么这样就会呈现脏读。

flushCache设置如下:             

                       
                               
                                       
                                               
                                                               
       
       

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理