天天看點

Mybatis

什麼是 MyBatis?

MyBatis 是一款優秀的持久層架構,它支援自定義 SQL、存儲過程以及進階映射。

MyBatis 免除了幾乎所有的 JDBC 代碼以及設定參數和擷取結果集的工作。

MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為資料庫中的記錄。

第一個Mybatis程式

建立資料庫環境

CREATE TABLE `user`(
   `id`  int(20) not NULL PRIMARY KEY,
	 `name` VARCHAR(50) DEFAULT NULL,
	 `pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;


INSERT into `user`(`id`,`name`,`pwd`) VALUES
(1,'ylc','123'),
(2,'ww','123'),
(3,'張三','123')
           

建立一個項目

1.建一個普通的maven項目

2.删除src目錄,作為父工程使用

3.去官網下載下傳Mysql驅動jar包,在pom檔案中寫入

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
</dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ylc</groupId>
    <artifactId>Mybatis-study</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>Mybatis-01</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>
           

建立一個子產品

編寫Mybatis的核心配置檔案mybatis-config.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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://waiwanga.mysql.rds.aliyuncs.com:3306?useSSL=true&amp;userUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value=""/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
     <mappers>
        <mapper resource="com/ylc/Dao/UserMapper.xml"/>
    </mappers>
</configuration>
           

編寫Mybatis工具類MybatisUtils

public class MybatisUtils {
    public  static SqlSessionFactory sqlSessionFactory=null;
    static {

        try {
            //使用Mybatis擷取sqlSessionFactory對象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
             sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
    //有了sqlSessionFactory就可以建立執行個體了
    public  static SqlSession getSqlSeeionFactory()
    {
        SqlSession sqlSession = sqlSessionFactory.openSession();//這裡設定為true可以自動送出事務
        return  sqlSession;
    }
}
           

編寫代碼

實體類

package com.ylc.pojo;

public class User {
    private  int id;
    private  String name;
    private  String pwd;

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
           

Dao接口

public interface UserDao {
    List<User> getUserlist();
}
           

接口實作配置檔案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綁定一個對應的Dao、Mapper接口 resultType;傳回結果類型-->
<mapper namespace="com.ylc.Dao.UserDao">
    <select id="getUserlist" resultType="com.ylc.pojo.User">
        select * from student.user
    </select>
</mapper>
           

student是資料庫名

生成測試類

public class UserDaoTest{
    @Test
    public void testGetUserlist() {
        //擷取sqlSeeionFactory對象
        SqlSession sqlSeeionFactory = MybatisUtils.getSqlSeeionFactory();
        //執行sql
        UserDao mapper = sqlSeeionFactory.getMapper(UserDao.class);

         //方式二
        //List<User> userlist = sqlSeeionFactory.selectList("com.ylc.Dao.UserDao.getUserlist");
        List<User> userlist = mapper.getUserlist();
        for (User user : userlist) {
            System.out.println(user);
        }
        sqlSeeionFactory.close();

    }
}
           

整體項目結構

Mybatis
Mybatis

增删改實作

接口

public interface UserDao {
    List<User> getUserlist();

    User getUserByID(int id);

    int AddUser(User user);

    int UpdateUser(User user);

    int DeleteUser(int id);
}
           

Select

  • id:就是namespace中的方法名
  • resultType:sql語句執行的傳回值
  • parameterType:傳入參數類型
    <select id="getUserByID" resultType="com.ylc.pojo.User" parameterType="int">
            select * from student.user where id = #{id}
        </select>
               
    @Test
    public void testTestGetUserByID() {
        SqlSession sqlSeenFactors = MybatisUtils.getSqlSeeionFactory();
        UserDao mapper = sqlSeenFactors.getMapper(UserDao.class);
        User userById = mapper.getUserByID(1);
        System.out.println(userById);
    
        sqlSeenFactors.close();
    }
               
Mybatis

Insert

<insert id="AddUser"  parameterType="com.ylc.pojo.User" >
    insert into student.user(id,name,pwd) values(#{id},#{name},#{pwd});
</insert>
           
@Test
    public void addUser() {
        User user1=new User(4,"yy","123456");
        SqlSession sqlSeenFactors = MybatisUtils.getSqlSeeionFactory();
        UserDao mapper = sqlSeenFactors.getMapper(UserDao.class);
        Object object = mapper.AddUser(user1);
        sqlSeenFactors.commit();
        System.out.println(object);
        sqlSeenFactors.close();
    }
           
Mybatis

Update

<update id="UpdateUser" parameterType="com.ylc.pojo.User">
    update student.user set name=#{name},pwd=#{pwd}  where id=#{id};
</update>
           
public  void  UpdateUser()
{
    SqlSession sqlSeeionFactory = MybatisUtils.getSqlSeeionFactory();
    UserDao mapper = sqlSeeionFactory.getMapper(UserDao.class);
    System.out.println(mapper);
    int updateUser = mapper.UpdateUser(new User(3, "yyy", "11111"));
    sqlSeeionFactory.commit();
    System.out.println(updateUser);
    sqlSeeionFactory.close();
}
           

Delete

<delete id="DeleteUser" parameterType="int">
    delete from student.user where id=#{id};
</delete>
           
@Test
public void  DeleteUser()
{
    SqlSession sqlSeeionFactory = MybatisUtils.getSqlSeeionFactory();
    UserDao mapper = sqlSeeionFactory.getMapper(UserDao.class);
    int i = mapper.DeleteUser(4);
    sqlSeeionFactory.commit();
    System.out.println(i);
    sqlSeeionFactory.close();
}
           

核心配置檔案

mybatis-config.xml

configuration(配置)
properties(屬性)
settings(設定)
typeAliases(類型别名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境配置)
environment(環境變量)
transactionManager(事務管理器)
dataSource(資料源)
databaseIdProvider(資料庫廠商辨別)
mappers(映射器)
           

環境變量environment

Mybatis可以配置多個環境,但是SqlsessionFactory執行個體隻能選擇一個環境

預設環境設定,更改

default

為其他

environment

的id就可以了

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://waiwanga.mysql.rds.aliyuncs.com:3306?useSSL=true&amp;userUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="username" value="root"/>
            <property name="password" value="19990729Yy"/>
        </dataSource>
    </environment>
    <environment id="text">
        <transactionManager type=""></transactionManager>
        <dataSource type=""></dataSource>
    </environment>
</environments>
           
transactionManager事務管理器

JDBC

MANAGED

兩種類型:

  • JDBC:使用了JDBC的送出和復原
  • MANAGED:配置幾乎沒做什麼, 預設情況下它會關閉連接配接
dataSource資料源

有三種資料類型:

UNPOOLED

POOLED

JNDI

預設的是

POOLED

property屬性

可以通過properties來引用配置檔案

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
           

在核心配置檔案中映入

<!--引入外部配置檔案-->
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="pwd" value="11111"/>
    </properties>
           
  • 可以直接引入外部檔案
  • 可以在其中增加一些屬性配置
  • 如果兩個檔案有同一個字段,優先使用外部配置檔案的!
類型别名typeAliases

存在的意義僅在于用來減少類完全限定名的備援

<!--可以給實體類起别名-->
<typeAliases>
    <typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
           

也可以指定一個包名,MyBatis 會在包名下面搜尋需要的 Java Bean,比如:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>
           

每一個在包

domain.blog

中的 Java Bean,在沒有注解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的别名。 比如

domain.blog.Author

的别名為

author

;若有注解,則别名為其注解值。見下面的例子:

@Alias("author")
public class Author {
}
           
設定Setting
Mybatis

映射器Mappers

注冊綁定Mapper檔案

方式一:對于相對路徑的資源引用

<mappers>
    <mapper resource="com/ylc/Dao/UserMapper.xml"/>
</mappers>
           

方式二:通過Class檔案綁定注冊

接口和Mapper檔案必須同名和在同一個包下

<mappers>
    <mapper resource="com.ylc.Dao.UserMapper"/>
</mappers>
           

方式三:使用掃描包注入

<!--每一個Mapper.XML都需要在Mybatis核心配置檔案中注冊!-->
<mappers>
    <package name="com.ylc.dao"/>
</mappers>
           

生命周期和作用域

Mybatis

生命周期和作用域,是至關重要的,因為錯誤的使用會導緻非常嚴重的并發問題。

SqlSessionFactoryBuilder:

  • 一旦建立了 SqlSessionFactory,就不再需要它了
  • 局部變量

SqlSessionFactory:

  • 說白了就是可以想象為 :資料庫連接配接池
  • SqlSessionFactory 一旦被建立就應該在應用的運作期間一直存在,沒有任何理由丢棄它或重新建立另一個執行個體。
  • 是以 SqlSessionFactory 的最佳作用域是應用作用域。
  • 最簡單的就是使用單例模式或者靜态單例模式。

SqlSession

  • 連接配接到連接配接池的一個請求!
  • SqlSession 的執行個體不是線程安全的,是以是不能被共享的,是以它的最佳的作用域是請求或方法作用域。
  • 用完之後需要趕緊關閉,否則資源被占用!
Mybatis

這裡面的每一個Mapper,就代表一個具體的業務!

結果映射

資料庫中字段和實體類中字段不對應,會找不到資料,因為類型處理器會直接找資料庫中的字段

解決方法:起别名

<select id="getUserById" resultType="com.ylc.pojo.User">
    select id,name,pwd as password from mybatis.user where id = #{id}
</select>
           

但是這個方法還不太靈活

<!--結果集映射-->
<resultMap id="UserMap" type="User">
    <!--column資料庫中的字段,property實體類中的屬性-->
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="UserMap">
    select * from mybatis.user where id = #{id}
</select>
           
  • resultMap

    元素是 MyBatis 中最重要最強大的元素
  • ResultMap 的設計思想是,對于簡單的語句根本不需要配置顯式的結果映射,而對于複雜一點的語句隻需要描述它們的關系就行了。
  • ResultMap

    最優秀的地方在于,雖然你已經對它相當了解了,但是根本就不需要顯式地用到他們。

日志

如果資料庫發生了異常怎麼可以檢視到,需要排錯,這時候就需要日志

Mybatis

具體在核心配置檔案中設定,但注意日志名稱不能輸入錯誤

标準STDOUT_LOGGING日志配置:

<settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
           
Mybatis

可以檢視到資料庫連接配接情況,以及sql語句和傳入的參數,以及傳回行數

LOG4J

什麼是Log4j?

  • Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日志資訊輸送的目的地是控制台、檔案、GUI元件
  • 我們也可以控制每一條日志的輸出格式;
  • 通過定義每一條日志資訊的級别,我們能夠更加細緻地控制日志的生成過程。
  • 通過一個配置檔案來靈活地進行配置,而不需要修改應用的代碼。

導入Log4j包,在Maven官網找到包

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
           

log4j配置檔案

#将等級為DEBUG的日志資訊輸出到console和file這兩個目的地,console和file的定義在下面的代碼
log4j.rootLogger=DEBUG,console,file

#控制台輸出的相關設定
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#檔案輸出的相關設定
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志輸出級别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
           

配置實作

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>
           

最後效果

Mybatis

基本使用

如果要使用LOG4J,需要導入import org.apache.log4j.Logger;

日志對象,參數為目前類的class

static Logger logger = Logger.getLogger(UserDaoTest.class);
           
public class UserDaoTest {
    static Logger logger = Logger.getLogger(UserDaoTest.class);
    @Test
    public  void  TextLOG4J()
    {
        logger.info("進入了TextLOG4J");
        logger.debug("進入了TextLOG4J");
        logger.error("進入了TextLOG4J");
    }
}
           
Mybatis

分頁

使用limit分頁

void gerLimitUser();
           

Mapper.xml

<select id="gerLimitUser" parameterType="map" resultType="com.ylc.pojo.User">
        select * from student.user limit  #{startindex},#{pagesize}
    </select>
           

測試

@Test
    public  void  gerLimitUser()
    {
        SqlSession sqlSeeionFactory = MybatisUtils.getSqlSeeionFactory();
        UserDao mapper = sqlSeeionFactory.getMapper(UserDao.class);

        HashMap<String, Integer> map = new HashMap<>();
        map.put("startindex",2);
        map.put("pagesize",2);
        List<User> users = mapper.gerLimitUser(map);
        for (User user : users) {
            System.out.println(user);
        }
        sqlSeeionFactory.close();
    }
           
Mybatis

使用RowBounds分頁

不再使用SQL實作分頁

List<User> getUserByRowBounds();
           

mapper.xml

<select id="getUserByRowBounds" resultMap="UserMap">
    select * from  student.user
</select>
           
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds實作
RowBounds rowBounds = new RowBounds(0, 2);

List<User> userList = sqlSession.selectList("com.ylc.dao.UserMapper.getUserByRowBounds",null,rowBounds);

    for (User user : userList) {
    System.out.println(user);
    }
    sqlSession.close();
    }
           

使用插件分頁

PageHelper

使用注解開發

在接口上寫一個注解

@Select("select * from student.user")
List<User> getUsers();
           
@Test
    public  void  getUsers()
    {
        SqlSession sqlSeeionFactory = MybatisUtils.getSqlSeeionFactory();
        UserDao mapper = sqlSeeionFactory.getMapper(UserDao.class);
        List<User> users = mapper.getUsers();
        for (User user : users) {
            System.out.println(user);
        }
    }
           

這樣的注解适合簡單的sql語句,複雜的還是适合放在XMl裡面

Lombok

Project Lombok是一個java庫,它可以自動插入編輯器和建構工具,為java增添樂趣。永遠不要再編寫另一個getter或equals方法,隻要有一個注釋,您的類就有一個功能齊全的建構器、自動記錄變量等等。

比如我們的類中手寫了大量的get、set方法,Lombok可以幫助我們簡化操作

安裝Lombok

Mybatis

導入LomBoK的jar包

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    <scope>provided</scope>
</dependency>
           
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger
@Data  //無參構造、get set、hascode、ToString
@Builder
@Singular
@Delegate
@Value
@Accessors
@Wither
@SneakyThrows
           

測試:

在我們類名前面加上@Data就可以自動生成相應的結構

Mybatis

優點:

能通過注解形式自定生成構造器:無參構造、get set、hascode、ToString等等

讓代碼更加簡潔,便于維護

缺點:

不支援多種參數構造去重載

降低了源代碼的可讀性和完整性

多對一

關聯查詢

配置檔案

<?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>
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://waiwanga.mysql.rds.aliyuncs.com:3306?useSSL=true&amp;userUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="19990729Yy"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="StudentMapper.xml"/>
        <mapper resource="TeacherMapper.xml"/>
    </mappers>
</configuration>
           

proj類

package com.ylc.pojo;
import lombok.Data;

@Data
public class Student {
    private  int id;
    private String name;
    private  Teacher teacher;
}
           
package com.ylc.pojo;
import lombok.Data;

@Data
public class Teacher {
    private  int id;
    private  String name;
}
           

StudentMapper.xml

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

    <select id="getStudent" resultMap="StudentTeacher">
      select * from student.student
    </select>

    <resultMap id="StudentTeacher" type="com.ylc.pojo.Student">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <association property="teacher" column="tid" javaType="com.ylc.pojo.Teacher" select="getTeacher">
        </association>
    </resultMap>

    <select id="getTeacher" resultType="com.ylc.pojo.Teacher">
        select * from student.teacher where id = #{id}
    </select>
</mapper>
           

測試類

@Test
    public void getStudent()
    {
        SqlSession sqlSeeionFactory = MybatisUtils.getSqlSeeionFactory();
        StudentMapper mapper = sqlSeeionFactory.getMapper(StudentMapper.class);
        List<Student> student = mapper.getStudent();
        for (Student student1 : student) {
            System.out.println(student1);
        }
        sqlSeeionFactory.close();
    }
           
Mybatis

嵌套查詢

<select id="getstudent2" resultMap="StudentTeacher2">
        select s.id sid, s.name sname, t.name tname,t.id tid
        from student.student s,student.teacher t
        where s.tid = t.id ;
    </select>
    <resultMap id="StudentTeacher2" type="com.ylc.pojo.Student">
        <result property="id" column="sid"></result>
        <result property="name" column="sname"></result>
        <association property="teacher" javaType="com.ylc.pojo.Teacher">
            <result property="name" column="tname"></result>
        </association>
    </resultMap>
           
@Test
public  void  getstudent2()
{
    SqlSession sqlSeeionFactory = MybatisUtils.getSqlSeeionFactory();
    StudentMapper mapper = sqlSeeionFactory.getMapper(StudentMapper.class);
    List<Student> student = mapper.getstudent2();
    for (Student student1 : student) {
        System.out.println(student1);
    }
    sqlSeeionFactory.close();
}
           

一對多

@Data
public class Teacher {
    private  int id;
    private  String name;
    private List<Student> studentList;
}
           
@Data
public class Student {
    private  int id;
    private String name;
    private  int  tid;
}
           
public interface TeacherMapper {
    Teacher getTeacher(@Param("tid") int id);
}
           
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ylc.Dao.TeacherMapper">

    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid, s.name sname, t.name tname,t.id tid
        from student.student s,student.teacher t
        where s.tid = t.id and t.id = #{tid}
    </select>
    <resultMap id="TeacherStudent" type="com.ylc.pojo.Teacher">
        <result column="tid" property="id"></result>
        <result column="tname" property="name"></result>
        <collection property="studentList"  ofType="com.ylc.pojo.Student">
            <result column="sid" property="id"></result>
            <result column="sname" property="name"></result>
            <result column="tid" property="tid"></result>
        </collection>
    </resultMap>
</mapper>
           
@Test
public void getTeacher() {
    SqlSession sqlSeeionFactory = MybatisUtils.getSqlSeeionFactory();
    TeacherMapper mapper = sqlSeeionFactory.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher);
    sqlSeeionFactory.close();
}
           
Mybatis

動态SQL

IF

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from mybatis.blog where 1=1
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</select>
           

choose (when, otherwise)

<select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                </when>
                <when test="author != null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views = #{views}
                </otherwise>
            </choose>
        </where>
    </select>
           

trim (where,set)

select * from mybatis.blog
<where>
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</where>
           
<update id="updateBlog" parameterType="map">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>

           

所謂的動态SQL,本質還是SQL語句 , 隻是我們可以在SQL層面,去執行一個邏輯代碼

SQL片段

有的時候,我們可能會将一些功能的部分抽取出來,友善複用!

  1. 使用SQL标簽抽取公共的部分
    <sql id="if-title-author">
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </sql>
               
  2. 在需要使用的地方使用Include标簽引用即可
    <select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <include refid="if-title-author"></include>
        </where>
    </select>
               

注意事項:

  • 最好基于單表來定義SQL片段!
  • 不要存在where标簽

Foreach

select * from user where 1=1 and 

  <foreach item="id" collection="ids"
      open="(" separator="or" close=")">
        #{id}
  </foreach>

(id=1 or id=2 or id=3)

           
Mybatis
<!--
        select * from mybatis.blog where 1=1 and (id=1 or id = 2 or id=3)

        我們現在傳遞一個萬能的map , 這map中可以存在一個集合!
-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from mybatis.blog

    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id = #{id}
        </foreach>
    </where>

</select>

           

緩存

經常查詢并且不經常改變的資料。可以使用緩存

MyBatis系統中預設定義了兩級緩存:一級緩存和二級緩存

  • 預設情況下,隻有一級緩存開啟。(SqlSession級别的緩存,也稱為本地緩存)
  • 二級緩存需要手動開啟和配置,他是基于namespace級别的緩存。
  • 為了提高擴充性,MyBatis定義了緩存接口Cache。我們可以通過實作Cache接口來自定義二級緩存

緩存失效的情況:

  1. 查詢不同的東西
  2. 增删改操作,可能會改變原來的資料,是以必定會重新整理緩存!
    Mybatis
  3. 查詢不同的Mapper.xml
  4. 手動清理緩存!
    Mybatis

小結:一級緩存預設是開啟的,隻在一次SqlSession中有效,也就是拿到連接配接到關閉連接配接這個區間段!

一級緩存就是一個Map。

二級緩存

  • 二級緩存也叫全局緩存,一級緩存作用域太低了,是以誕生了二級緩存
  • 基于namespace級别的緩存,一個名稱空間,對應一個二級緩存;
  • 工作機制
    • 一個會話查詢一條資料,這個資料就會被放在目前會話的一級緩存中;
    • 如果目前會話關閉了,這個會話對應的一級緩存就沒了;但是我們想要的是,會話關閉了,一級緩存中的資料被儲存到二級緩存中;
    • 新的會話查詢資訊,就可以從二級緩存中擷取内容;
    • 不同的mapper查出的資料會放在自己對應的緩存(map)中;

步驟:

  1. 開啟全局緩存
    <!--顯示的開啟全局緩存-->
    <setting name="cacheEnabled" value="true"/>
               
  2. 在要使用二級緩存的Mapper中開啟
    <!--在目前Mapper.xml中使用二級緩存-->
    <cache/>
               
    也可以自定義參數
    <!--在目前Mapper.xml中使用二級緩存-->
    <cache  eviction="FIFO"
           flushInterval="60000"
           size="512"
           readOnly="true"/>
               
    1. 問題:我們需要将實體類序列化!否則就會報錯!
      Caused by: java.io.NotSerializableException: com.kuang.pojo.User
                 

小結:

  • 隻要開啟了二級緩存,在同一個Mapper下就有效
  • 所有的資料都會先放在一級緩存中;
  • 隻有當會話送出,或者關閉的時候,才會送出到二級緩沖中!
  • 開啟二級緩存需要序列化對象

緩存原理

Mybatis

自定義緩存-ehcache

Ehcache是一種廣泛使用的開源Java分布式緩存。主要面向通用緩存
           

要在程式中使用ehcache,先要導包!

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version>
</dependency>
           

在mapper中指定使用我們的ehcache緩存實作!

<!--在目前Mapper.xml中使用二級緩存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
           

ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--
       diskStore:為緩存路徑,ehcache分為記憶體和磁盤兩級,此屬性定義磁盤的緩存位置。參數解釋如下:
       user.home – 使用者主目錄
       user.dir  – 使用者目前工作目錄
       java.io.tmpdir – 預設臨時檔案路徑
     -->
    <diskStore path="./tmpdir/Tmp_EhCache"/>
    
    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>
 
    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
    <!--
       defaultCache:預設緩存政策,當ehcache找不到定義的緩存時,則使用這個緩存政策。隻能定義一個。
     -->
    <!--
      name:緩存名稱。
      maxElementsInMemory:緩存最大數目
      maxElementsOnDisk:硬碟最大緩存個數。
      eternal:對象是否永久有效,一但設定了,timeout将不起作用。
      overflowToDisk:是否儲存到磁盤,當系統當機時
      timeToIdleSeconds:設定對象在失效前的允許閑置時間(機關:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,預設值是0,也就是可閑置時間無窮大。
      timeToLiveSeconds:設定對象在失效前允許存活時間(機關:秒)。最大時間介于建立時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,預設是0.,也就是對象存活時間無窮大。
      diskPersistent:是否緩存虛拟機重新開機期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskSpoolBufferSizeMB:這個參數設定DiskStore(磁盤緩存)的緩存區大小。預設是30MB。每個Cache都應該有自己的一個緩沖區。
      diskExpiryThreadIntervalSeconds:磁盤失效線程運作時間間隔,預設是120秒。
      memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache将會根據指定的政策去清理記憶體。預設政策是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
      clearOnFlush:記憶體數量最大時是否清除。
      memoryStoreEvictionPolicy:可選政策有:LRU(最近最少使用,預設政策)、FIFO(先進先出)、LFU(最少通路次數)。
      FIFO,first in first out,這個是大家最熟的,先進先出。
      LFU, Less Frequently Used,就是上面例子中使用的政策,直白一點就是講一直以來最少被使用的。如上面所講,緩存的元素有一個hit屬性,hit值最小的将會被清出緩存。
      LRU,Least Recently Used,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離目前時間最遠的元素将被清出緩存。
   -->

</ehcache>