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>

第五步:創建pojo實體類

package 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] %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包,創建日志對象,把信息寫到日志文件中,盡量每天一個日志文件,把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()); } } 

7.resultMapparameterMap

7.1復雜條件的查詢舉例

當參數不屬於同一個對象時,使用簡單的查詢非常麻煩,那么可以把一個對象作為另一個對象的屬性結合使用:

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

 12.異常問題

1.Mybatis異常 There is no getter for property...

在dao層修改:

List<Article> recommandList(@Param("siteid") Integer siteid);

如上修改,給siteid @Param注入getter 即可。

總結:如果xml的parameterType屬性時Map,則不需要加@Param。如果是一個int,string這種類型的參數,需要在Dao層加@Param注解;只有一個參數,xml不用if,可不加。有多個參數,dao層每個參數都要加上@Param。如果傳入的參數特別多,建議直接傳入一個Map。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM