Mybatis学习笔记

# 简介

什么是Mybatis

img

maven仓库

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

Github:https://hub.fastgit.org/mybatis/mybatis-3

持久化

数据持久化

将程序的数据在持久状态和瞬时状态转化的过程

内存:断电即失

数据库jdbc io文件持久化

持久层

Dao层,Service层,Controller层...

完成持久化工作的代码块

层界限十分明显

模块项目创建过程

搭建环境

1.创建数据库

2.新建maven项目

3.删除src目录,导入maven依赖

 1 <dependencies>
 2     <!--  Mysql驱动      -->
 3         <dependency>
 4             <groupId>mysql</groupId>
 5             <artifactId>mysql-connector-java</artifactId>
 6             <version>5.1.47</version>
 7         </dependency>
 8     <!--  Mybatis      -->
 9         <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
10         <dependency>
11             <groupId>org.mybatis</groupId>
12             <artifactId>mybatis</artifactId>
13             <version>3.5.7</version>
14         </dependency>
15     <!--  junit      -->
16         <dependency>
17             <groupId>junit</groupId>
18             <artifactId>junit</artifactId>
19             <version>4.12</version>
20         </dependency>
21     </dependencies>

创建模块

编写Mybatis核心配置文件

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration
 3         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 5 <!--核心配置文件-->
 6 <configuration>
 7 
 8     <environments default="development">
 9         <environment id="development">
10             <transactionManager type="JDBC"/>
11             <dataSource type="POOLED">
12                 <property name="driver" value="com.mysql.jdbc.Driver"/>
13                 <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
14                 <property name="username" value="root"/>
15                 <property name="password" value="123456"/>
16             </dataSource>
17         </environment>
18     </environments>
19 
20 </configuration>

编写Mybatis工具类

 1 //sqlSessionFactory--->sqlSession
 2 public class MybatisUtils {
 3     public static SqlSessionFactory sqlSessionFactory ;
 4     static {
 5         try {
 6             //使用Mybatis第一步 获取sqlSessionFactory对象
 7             String resource = "mybatis-config.xml";
 8             InputStream  inputStream = Resources.getResourceAsStream(resource);
 9             sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
10         } catch (IOException e) {
11             e.printStackTrace();
12         }
13 
14     }
15     //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
16     // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
17     public static SqlSession getSqlSession(){
18         return  sqlSessionFactory.openSession();
19     }
20 }

编写代码

实体类

 1 package com.kuang.pojo;
 2 //实体类
 3 public class User {
 4     private int id;
 5     public String name;
 6     public String pwd;
 7 
 8     public User(int id, String name, String pwd) {
 9         this.id = id;
10         this.name = name;
11         this.pwd = pwd;
12     }
13 
14     public User() {
15     }
16 
17     public int getId() {
18         return id;
19     }
20 
21     public void setId(int id) {
22         this.id = id;
23     }
24 
25     public String getName() {
26         return name;
27     }
28 
29     public void setName(String name) {
30         this.name = name;
31     }
32 
33     public String getPwd() {
34         return pwd;
35     }
36 
37     public void setPwd(String pwd) {
38         this.pwd = pwd;
39     }
40 
41     @Override
42     public String toString() {
43         return "User{" +
44                 "id=" + id +
45                 ", name='" + name + '\'' +
46                 ", pwd='" + pwd + '\'' +
47                 '}';
48     }
49 }

Dao接口

1 public interface UserDao {
2     List<User> getUserList();
3 }

接口实现类(由原理UserDaoImpl转换为一个Mapper配置文件)

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper
 3         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5 <!--namespace=绑定一个对应的Dao/Mappe接口-->
 6 <mapper namespace="com.kuang.dao.UserDao">
 7     <select id="getUserList" resultType="com.kuang.pojo.User">
 8         select * from mybatis.uers
 9     </select>
10 
11 </mapper>

测试

注意点:org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.

    类型接口Dao是未知的在MapperRegistry中

<!--每个Mapper.XML都需要在Mybatis核心配置文件中注册-->
Maven导出资源 pom打包文件
 1  
 2 <build>
 3         <finalName>test</finalName>
 4         <resources>
 5             <resource>
 6                 <directory>src/main/resources</directory>
 7                 <includes>
 8                     <include>**/*.properties</include>
 9                     <include>**/*.xml</include>
10                     <include>**/*.tld</include>
11                 </includes>
12                 <filtering>false</filtering>
13             </resource>
14             <resource>
15                 <directory>src/main/java</directory>
16                 <includes>
17                     <include>**/*.properties</include>
18                     <include>**/*.xml</include>
19                     <include>**/*.tld</include>
20                 </includes>
21                 <filtering>false</filtering>
22             </resource>
23         </resources>
24     </build>

junit测试

@Test
    public void test(){
        //第一步获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL 方式1:getMapper
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        //关闭SqlSession
        sqlSession.close();
    }

img

CRUD的实现及问题

注意事项:namespace中的包名要和Dao/mapper接口的包名一致

Select

查询语句

id:对应的namespace中的方法名;

resultType:sql语句执行的返回值!

parameterType:参数类型

1.编写接口

2.编写对应的mapper中的sql语句

3.测试

 1 public interface UserMapper {
 2     //查询全部用户
 3     List<User> getUserList();
 4 
 5     //根据id查询用户
 6     User getUserById(int id);
 7 
 8     //insert一个用户
 9     int addUser(User user);
10 
11     //update一个用户
12     int updateUser(User user);
13 
14     //delete用户
15     int deleteUser(int id);
16 }
 1 mapper namespace="com.kuang.dao.UserMapper">
 2     <select id="getUserList" resultType="com.kuang.pojo.User">
 3         select * from mybatis.user
 4     </select>
 5 
 6     <select id="getUserById" resultType="com.kuang.pojo.User" parameterType="int">
 7         select * from mybatis.user where id = #{id};
 8     </select>
 9 
10     <insert id="addUser" parameterType="com.kuang.pojo.User">
11         insert into mybatis.user(id,name,pwd)value (#{id},#{name},#{pwd});
12     </insert>
13 
14     <update id="updateUser" parameterType="com.kuang.pojo.User">
15         update mybatis.user set name =#{name},pwd=#{pwd} where id=#{id};
16     </update>
17 
18     <delete id="deleteUser" parameterType="int">
19         delete from mybatis.user where id =#{id};
20     </delete>
21 </mapper>

test

 1     @Test
 2     public void test(){
 3         //第一步获得SqlSession对象
 4         SqlSession sqlSession = MybatisUtils.getSqlSession();
 5         //执行SQL 方式1:getMapper
 6         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
 7         List<User> userList = userMapper.getUserList();
 8         for (User user : userList) {
 9             System.out.println(user);
10         }
11         //关闭SqlSession
12         sqlSession.close();
13     }
14 
15     @Test
16     public void getUserById(){
17         SqlSession sqlSession = MybatisUtils.getSqlSession();
18 
19         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
20         User user = mapper.getUserById(1);
21         System.out.println(user);
22 
23         sqlSession.close();
24     }
25     //增删改需要提交事务
26     @Test
27     public void addUser(){
28         SqlSession sqlSession = MybatisUtils.getSqlSession();
29 
30         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
31         mapper.addUser(new User(5,"小明","1562344"));
32         sqlSession.commit();
33         sqlSession.close();
34     }
35     @Test
36     public void updateUser(){
37         SqlSession sqlSession = MybatisUtils.getSqlSession();
38 
39         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
40         mapper.updateUser(new User(4,"小蓝","6554332"));
41         sqlSession.commit();
42         sqlSession.close();
43     }
44     @Test
45     public void deleteUser(){
46         SqlSession sqlSession = MybatisUtils.getSqlSession();
47         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
48         mapper.deleteUser(4);
49         sqlSession.commit();
50         sqlSession.close();
51     }

相关问题:

增删改要提交事务

java.lang.ExceptionInInitializerErrorCreate breakpoint
at com.kuang.dao.UserDaoTest.updateUser(UserDaoTest.java:48)
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in com/kuang/dao/UserMapper.xml

找不到‘id’ 原因为在下面这段代码中将parameterType="int"写成了parameterType="id" 从而没找到id这个参数

<delete id="deleteUser" parameterType="int">

Map和SQL模糊查询实现问题

img

img

img

配置解析

mybatis核心配置文件

mybatis-config.xml

configuration(配置)    

properties(属性)    

settings(设置)    

typeAliases(类型别名)    

typeHandlers(类型处理器)    

objectFactory(对象工厂)    

plugins(插件)        

nvironments(环境配置)            

environment(环境变量)            

transactionManager(事务管理器)    

dataSource(数据源)    

databaseIdProvider(数据库厂商标识)    

mappers(映射器)

环境配置(properties)

MyBatis 可以配置成适应多种环境

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

Mybatis 默认的事务管理器是JDBC,连接池:POOLED

属性优化

我们可以通过properties属性来引用配置文件

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。 (db.properties)

编写一个配置文件

db.properties

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

在核心配置文件中引入

mybatis-config.xml (同时有的话,优先走外面properties)

<?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>

    <!--引入外部配置文件-->
    <!--<properties resource="db.properties"/>-->

    <properties resource="db.properties">
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </properties>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--每一个mapper.xml都需要注册-->
    <mappers>
        <mapper resource="com/hou/dao/UserMapper.xml"/>
    </mappers>

</configuration>

别名优化

类型别名可为 Java 类型设置一个缩写名字。

<typeAliases>
    <typeAlias type="com.kuang.pojo.User" alias="User"></typeAlias>
</typeAliases>

扫描实体类的包,默认别名就为这个类的类名首字母小写

<typeAliases>
    <package name="com.kuang.pojo"></package>
</typeAliases>

在实体类,比较少的时候使用第一种,实体类多使用第二种。

第一种可以自定义,第二则不行,但是 若有注解,则别名为其注解值 。

@Alias("hello")
public class User {
}

映射器说明

  1. 设置
设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置

其他

映射器

方式一: [推荐使用]

<mappers>
    <mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>

方式二:

<mappers>
    <mapper class="com.kuang.dao.UserMapper" />
</mappers>
  • 接口和它的Mapper必须同名
  • 接口和他的Mapper必须在同一包下

方式三:

<mappers>
    <!--<mapper resource="com/kuang/dao/UserMapper.xml"/>-->
    <!--<mapper class="com.kuang.dao.UserMapper" />-->
    <package name="com.kuang.dao" />
</mappers>
  • 接口和它的Mapper必须同名
  • 接口和他的Mapper必须在同一包下

生命周期和作用域

作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:

  • 一旦创建了 SqlSessionFactory,就不再需要它了 。
  • 局部变量

SqlSessionFactory

  • 就是数据库连接池。
  • 一旦被创建就应该在应用的运行期间一直存在 ,没有任何理由丢弃它或重新创建另一个实例 。 多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。
  • 因此 SqlSessionFactory 的最佳作用域是应用作用域。
  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession

  • 每个线程都应该有它自己的 SqlSession 实例。
  • 连接到连接池的请求!
  • SqlSession 的实例不是线程安全的,因此是不能被共享的 ,所以它的最佳的作用域是请求或方法作用域。
  • 用完之后赶紧关闭,否则资源被占用。

解决属性名和字段名不一致的问题

数据库中的字段

image

新建一个项目,拷贝之前,测试实体字段不一致的情况

User

package com.kuang.pogo;

public class User {

    private int id;
    private String name;
    private String password;
}

问题:

image

解决方法:

核心配置文件

  • 起别名
<select id="getUserById" resultType="User"
    parameterType="int">
        select id,name,pwd as password from mybatis.user where id = #{id}
</select>
  • resultMap 结果集映射
<?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绑定一个对应的mapper接口-->
<mapper namespace="com.hou.dao.UserMapper">

    <select id="getUserById" resultMap="UserMap" parameterType="int">
        select * from mybatis.user where id = #{id}
    </select>

    <!--结果集映射-->
 

</mapper>
  • resultMap 元素是 MyBatis 中最重要最强大的元素。
  • ResultMap 的设计思想是,对简单的语句无需配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • 如果世界都那么简单就好了
<resultMap id="UserMap" type="User">
    <!--colunm 数据库中的字段,property实体中的属性-->
    <!--<result column="id" property="id"></result>-->
    <!--<result column="name" property="name"></result>-->
    <result column="pwd" property="password"></result>
</resultMap>

日志

1. 日志工厂

如果一个数据库操作出现了异常,我们需要排错。日志就是最好的助手。

曾经:sout,debug

现在:日志工厂

logImpl

  • SLF4J
  • LOG4J [掌握]
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING [掌握]
  • NO_LOGGING

具体使用哪一个,在设置中设定

STDOUT_LOGGING 标志日志输出

mybatis-confi中

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

2. Log4j

  1. 先导包

    pom.xml下

    <dependencies>
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
    
  2. 新建log4j.properties文件

### set log levels ###
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/hou.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
  1. 配置实现
<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>
  1. Log4j使用

分页

1. Limit 分页

语法:

SELECT * from user limit startIndex,pageSize;
SELECT * from user limit 0,2;
package com.hou.dao;

import com.hou.pojo.User;

import java.util.List;
import java.util.Map;

public interface UserMapper {

    //根据id查询用户
    User getUserById(int id);

    List<User> getUserByLimit(Map<String, Integer> map);

}

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绑定一个对应的mapper接口-->
<mapper namespace="com.hou.dao.UserMapper">

    <select id="getUserById" resultMap="UserMap"
    parameterType="int">
        select * from mybatis.user where id = #{id}
    </select>

    <!--结果集映射-->
    <resultMap id="UserMap" type="User">
        <!--colunm 数据库中的字段,property实体中的属性-->
        <!--<result column="id" property="id"></result>-->
        <!--<result column="name" property="name"></result>-->
        <result column="pwd" property="password"></result>
    </resultMap>

    <select id="getUserByLimit" parameterType="map"
            resultType="User" resultMap="UserMap">
      select * from mybatis.user limit #{startIndex},#{pageSize}
    </select>

</mapper>

test类

@Test
public void getByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex", 1);
    map.put("pageSize", 2);
    List<User> userList = mapper.getUserByLimit(map);

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

    sqlSession.close();
}

2. RowBounds分页

@Test

@Test
public void getUserByRow(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //RowBounds实现
    RowBounds rowBounds = new RowBounds(1, 2);

    //通过java代码层面
    List<User> userList = sqlSession.selectList
        ("com.hou.dao.UserMapper.getUserByRowBounds",
         null,rowBounds);

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

    sqlSession.close();
}

3. 分页插件

  • pageHelper

使用注解开发

  1. 删除 UserMapper.xml

  2. UserMapper

    package com.kuang.dao;
    
    import com.kuang.pojo.User;
    import org.apache.ibatis.annotations.Select;
    
    import java.util.List;
    
    public interface UserMapper {
    
        @Select("select * from user")
        List<User> getUsers();
    }
    
  3. 核心配置 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>
    
        <!--引入外部配置文件-->
        <properties resource="db.properties"/>
    
        <!--可以给实体类起别名-->
        <typeAliases>
            <typeAlias type="com.kuang.pojo.User" alias="User"></typeAlias>
        </typeAliases>
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>
    
        <!--绑定接口-->
        <mappers>
            <mapper class="com.kuang.dao.UserMapper"></mapper>
        </mappers>
    </configuration>
    

    本质:反射机制

    底层:动态代理!

image

Mybatis详细执行流程:

  1. Resource获取全局配置文件

  2. 实例化SqlsessionFactoryBuilder构造器

  3. 解析配置文件流XMLCondigBuilder

  4. Configration所有的配置信息

  5. SqlSessionFactory实例化

  6. trasactional事务管理

  7. 创建executor执行器

  8. 创建SqlSession

  9. 实现CRUD

  10. 查看是否执行成功

  11. 提交事务

  12. 关闭

注解CRUD

package com.kuang.dao;

import com.kuang.pojo.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {

    @Select("select * from user")
    List<User> getUsers();

    //方法存在多个参数,所有的参数必须加@Param
    @Select("select * from user where id = #{id}")
    User getUserById(@Param("id") int id);

    @Insert("insert into user (id, name, pwd) values" +
            "(#{id},#{name},#{password})")
    int addUser(User user);

    @Update("update user set name=#{name}, pwd=#{password} " +
            "where id=#{id}")
    int updateUser(User user);

    @Delete("delete from user where id=#{id}")
    int deleteUser(@Param("id") int id);

}

MybatisUtile

package com.kuang.utils;

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 java.io.IOException;
import java.io.InputStream;

//sqlSessionFactory --> sqlSession
public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            //使用mybatis第一步:获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession(){
        					//自动提交事务
        return sqlSessionFactory.openSession(true);
    }

}

Test【需要将接口注册绑定到核心配置文件中】

package com.kuang.dao;

import com.kuang.pojo.User;
import com.kuang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDaoTest {

    @Test
    public void test(){
        // 获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            // 1.执行 getmapper
            UserMapper userDao = sqlSession.getMapper(UserMapper.class);
            List<User> userList = userDao.getUsers();
            for (User user : userList) {
                System.out.println(user);
            }

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            //关闭
            sqlSession.close();
        }
    }

    @Test
    public void getuserById(){
        // 获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            // 1.执行 getmapper
            UserMapper userDao = sqlSession.getMapper(UserMapper.class);
            User user = userDao.getUserById(1);

            System.out.println(user);


        }catch(Exception e){
            e.printStackTrace();
        }finally{
            //关闭
            sqlSession.close();
        }
    }

    @Test
    public void addUser(){
        // 获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            // 1.执行 getmapper
            UserMapper userDao = sqlSession.getMapper(UserMapper.class);
            userDao.addUser(new User(6, "kun","123"));

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            //关闭
            sqlSession.close();
        }
    }

    @Test
    public void updateUser(){
        // 获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            // 1.执行 getmapper
            UserMapper userDao = sqlSession.getMapper(UserMapper.class);
            userDao.updateUser(new User(6, "fang","123"));

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            //关闭
            sqlSession.close();
        }
    }

    @Test
    public void deleteUser(){
        // 获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            // 1.执行 getmapper
            UserMapper userDao = sqlSession.getMapper(UserMapper.class);
            userDao.deleteUser(6);

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            //关闭
            sqlSession.close();
        }
    }
}

关于@Param()注解

  • 基本类型的参数或者String类型,需要加上

  • 引用类型不需要加

  • 如果只有一个基本类型可以忽略但是建议加上

  • 我们在sql中引用的就是我们这里的@Param("")中设定的属性名

    #{} ${}区别

Lombok

  1. 在IDEA中安装lombok插件

  2. 配置

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>
    
  3. @Getter and @Setter
    @FieldNameConstants
    @ToString
    @EqualsAndHashCode
    @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
    @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
    @Data
    @Builder
    @SuperBuilder
    @Singular
    @Delegate
    @Value
    @Accessors
    @Wither
    @With
    @SneakyThrows
    

    @Data: 无参构造,get,set,toString,hashCode

    在实体类上加注解

    package com.kuang.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
        private int id;
        private String name;
        private String password;
    
    }
    

多对一处理

image

  • 多个学生对应一个老师

  • 对于学生 关联 多个学生关联一个老师【多对一】

  • 对于老师,集合,一个老师有很多学生【一对多】

SQL:

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES ('1', '秦老师'); 

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明','1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

测试环境搭建

1.导入lombok

2.新建实体类Teacher,Student

3.建立Mapper接口

4.建立Mapper.XML文件

5.在核心配置文件中绑定注册Mapper接口或者文件

6.测试查询是否能够成功

按照查询嵌套处理

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

<resultMap id="StudentTeacher" type="com.kuang.pojo.Student">
    <result property="id" column="id"></result>
    <result property="name" column="name"></result>
    <!--对象使用assiociation-->
    <!--集合用collection-->
    <association property="teacher" column="tid"
                 javaType="com.kuang.pojo.Teacher"
                 select="getTeacher">
    </association>
</resultMap>

<select id="getTeacher" resultType="com.kuang.pojo.Teacher">
    select * from mybatis.teacher where id = #{id};
</select>

按照结果嵌套处理

    <!--===按照结果嵌套处理===-->
<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id sid,s.name sname,t.name tname,t.id tid
    from mybatis.student s,mybatis.teacher t
    where s.tid=t.id;
</select>
<resultMap id="StudentTeacher2" type="com.kuang.pojo.Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="com.kuang.pojo.Teacher">
        <result property="name" column="tname"/>
        <result property="id" column="tid"/>
    </association>
</resultMap>
<!--注意 javaType与type均要为实体类路径-->

子查询

联表查询

一对多处理

一个老师 拥有多个学生

测试环境搭建

实体类

@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}
@Data
public class Teacher {
    private int id;
    private String name;
    //一个老师有多个学生
    private List<Student> students;
}

按照查询嵌套处理

<select id="getTeacher" resultMap="TeacherStudent">
    select s.id sid, s.name sname, t.name tname, t.id tid
    from mybatis.student s, mybatis.teacher t
    where s.tid = t.id and t.id = #{id};
</select>
<resultMap id="TeacherStudent" type="com.kuang.pojo.Teacher">
    <result property="id" column="tid"></result>
    <result property="name" column="tname"></result>
    <!--集合中的泛型信息,我们用oftype获取-->
    <collection property="studentList" ofType="com.kuang.pojo.Student">
        <result property="id" column="sid"></result>
        <result property="name" column="sname"></result>
    </collection>
</resultMap>

按照结果嵌套处理

<resultMap id="TeacherStudent2" type="com.kuang.pojo.Teacher">    
    <collection property="studentList" column="id" javaType="ArrayList"                			                             ofType="com.hou.pojo.Student"                
                select="getStudentByTeacherId"></collection> 
</resultMap> 

<select id="getStudentByTeacherId" resultType="com.kuang.pojo.Student">
    select * from mybatis.student where tid = #{id} 
</select>
  1. 关联 - association 多对一
  2. 集合 - collection 一对多
  3. javaType & ofType
    1. JavaType用来指定实体中属性类型
    2. ofType映射到list中的类型,泛型中的约束类型

注意点:

  • 保证sql可读性,尽量保证通俗易懂
  • 注意一对多和多对一中属性字段问题
  • 如果问题不好排查错误,使用日志

image

动态sql

动态sql:根据不同的条件生成不同的SQL语句

1. 搭建环境

create table `blog`(
	`id` varchar(50) not null comment '博客id',
    `title` varchar(100) not null comment '博客标题',
    `author` varchar(30) not null comment '博客作者',
    `create_time` datetime not null comment '创建时间',
    `views` int(30) not null comment '浏览量'
	)ENGINE=InnoDB DEFAULT CHARSET=utf8

实体类

package com.hou.pojo;

import lombok.Data;

import java.util.Date;

@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

核心配置

<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

Mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.hou.mapper.BlogMapper">
    <insert id="addBlog" parameterType="Blog">
        insert into mybatis.blog (id, title, author, create_time, views) values
        (#{id}, #{title}, #{author}, #{create_time}, #{views});
    </insert>
</mapper>

新建随机生成ID包

package com.hou.utils;

import org.junit.Test;

import java.util.UUID;

@SuppressWarnings("all")
public class IDUtiles {

    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }

    @Test
    public void  test(){
        System.out.println(getId());
    }

}

测试类:添加数据

import com.hou.mapper.BlogMapper;
import com.hou.pojo.Blog;
import com.hou.utils.IDUtiles;
import com.hou.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.Date;

public class MyTest {

    @Test
    public void addBlog(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDUtiles.getId());
        blog.setAuthor("houdongun");
        blog.setCreateTime(new Date());
        blog.setViews(999);
        blog.setTitle("first");

        blogMapper.addBlog(blog);

        blog.setId(IDUtiles.getId());
        blog.setTitle("second");
        blogMapper.addBlog(blog);

        blog.setId(IDUtiles.getId());
        blog.setTitle("third");
        blogMapper.addBlog(blog);

        blog.setId(IDUtiles.getId());
        blog.setTitle("forth");
        blogMapper.addBlog(blog);

        sqlSession.close();
    }
}

2. 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 != author">
        and author = #{author}
    </if>
</select>

test

@Test
public void queryBlogIF(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();

    //        map.put("title", "second");
    map.put("author", "houdongun");

    List<Blog> list = blogMapper.queryBlogIF(map);

    for (Blog blog : list) {
        System.out.println(blog);
    }

    sqlSession.close();
}

3. 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>

4. trim、where、set

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

trim 可以自定义

SQL片段

有些时候我们有一些公共部分

  1. 使用sql便签抽取公共部分
  2. 在使用的地方使用include标签
<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

<select id="queryBlogIF" parameterType="map" resultType="Blog">
    select * from mybatis.blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

注意:

  • 最好基于单表
  • sql里不要存在where标签

5. for-each

<!--ids是传的,#{id}是遍历的-->
<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>

test

@Test
public void queryBlogForeach(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();

    ArrayList<Integer> ids = new ArrayList<Integer>();
    ids.add(1);
    ids.add(3);
    map.put("ids",ids);

    List<Blog> list = blogMapper.queryBlogForeach(map);

    for (Blog blog : list) {
        System.out.println(blog);
    }

    sqlSession.close();
}

缓存(了解)

1. 一级缓存

  1. 开启日志
  2. 测试一个session中查询两次相同记录。

缓存失效:

  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 查询不同的mapper.xml
  • 手动清除缓存

一级缓存默认开启,只在一次sqlseesion中有效

2. 二级缓存

  1. 开启全局缓存
<setting name="cacheEnabled" value="true"/>
  1. 在当前mapper.xml中使用二级缓存
<cache eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>

test

@Test
public void test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    SqlSession sqlSession1 = MybatisUtils.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = userMapper.queryUserByid(1);
    System.out.println(user);
    sqlSession.close();

    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    User user1 = userMapper1.queryUserByid(1);
    System.out.println(user1);
    System.out.println(user==user1);
    sqlSession1.close();
}

只用cache时加序列化

<cache/>

实体类

package com.hou.pojo;

import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {
    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;
    }
}

小结:

  • 只有开启了二级缓存,在Mapper下有效
  • 所有数据都会先放在一级缓存
  • 只有当回话提交,或者关闭的时候,才会提交到二级缓存

3. 自定义缓存-ehcache

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

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="java.io.tmpdir/Tmp_EhCache"/>
    <!--
       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,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->
    <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"/>

</ehcache>
posted @ 2021-09-24 17:04  TheFool1996  阅读(94)  评论(0)    收藏  举报