Mybatis 的知识学习

时间:2024-03-28 18:13:00

准备:

maven:https://mvnrepository.com/

mybatis: MyBatis中文网

1.mybatis的定义

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

持久化:

  1. 持久化是将程序数据在持久状态和瞬时状态间转换的机制。(理解一下这个持久状态转化为瞬时状态,持久化是mybatis最重要的特性)。即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)
  2. 持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。JDBC就是一种持久化机制文件IO也是一种持久化机制。

为什么要持久化:

  1. 因为内存断点后数据会丢失,但是有些业务不允许这种情况的存在。
  2. 比起硬盘,内存过于昂贵,如果有够量的内存,则不需要持久化服务,但是正是因为内存太贵,储存有限,因此需要持久化来缓存。

持久层:

  1. 完成持久化工作代码块,即Dao层
  2. 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。

mybatis的作用:

  1. Mybatis就是帮助程序员将数据存取到数据库里面。
  2. 传统的jdbc操作 , 有很多重复代码块,简化开发 。比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 。
  3. MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射。
  4. 所有的事情,不用Mybatis依旧可以做到,只是用了它,会更加方便更加简单,开发更快速。

mybatis的优点:

  1. 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  2. 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
  3. 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
  4. 提供xml标签,支持编写动态sql。
  5. 现在主流使用方法。

2.搭建mybatis

地址:https://mvnrepository.com/artifact/org.mybatis

依赖:

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

1.创建mysql数据库:

2.步骤

  1. 创建新项目,删除src

搭建环境(maven、数据库等)--->导入mybatis依赖-->编写java代码--->测试

  1. 导入mybatis依赖

Pom.xml:文件:

<?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">
    <parent>
        <artifactId>Mybatis-study</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mybatis-01</artifactId>
    <!--    maven资源过滤-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

mybatis的核心配置文件:连接数据的配置文件,包括账户、密码、时区、编码等配置。

<?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核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSl=trur&amp;sueUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

<!--    注册每一个mapper文件-->
    <mappers>
        <mapper resource="com/jiang/dao/UserMapper.xml"></mapper>
    </mappers>
    
</configuration>

  1. 创建新的模块

3.编写Mybatis的工具类:用于获取SqlSession对象:

package com.jiang.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;

/**
 * @author Lenovo
 * @date 2024/3/16
 * @time 15:53
 * @project Mybatis-study
 **/
public class MybatisUtils {


    private static SqlSessionFactory sqlSessionFactory;

    static {
        //1. 获取mybatis-config的配置文件
        String resource = "mybatis-config.xml";
        try {
            // 2. 获得SqlSessionFactory工厂:
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //既然有了sqlSessionFactory,顾名思义,我们就可以从中获得sqlSession的实例了
    //3.sqlSession 完全包含了面向数据库执行SQL命令所需的所有方法
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }

}

4.编写java代码:

(一) 编写实体类:

package com.jiang.pojo;

/**
 * @author Lenovo
 * @date 2024/3/16
 * @time 15:59
 * @project Mybatis-study
 **/
public class User {

    private int id;
    private String name;
    private String pwd;

    public User() {
    }

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

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getPwd() {
        return pwd;
    }

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

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

(二) 编写Dao接口:
package com.jiang.dao;

import com.jiang.pojo.User;

import java.util.List;

/**
 * @author Lenovo
 * @date 2024/3/16
 * @time 16:00
 * @project Mybatis-study
 **/
public interface UserDao {

    List<User> getUserList();
}

(三) 编写mapper文件:
<?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文件到mybatis-config文件中
<mapper namespace="com.jiang.dao.UserDao">
<!--    查询语句:id:是方法: resultType返回为User-->
    <select id="getUserList" resultType="com.jiang.pojo.User">
    select * from mybatis.user;
  </select>
</mapper>

  1. 编写测试类:
package com.jiang.dao;

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

import java.util.List;

/**
 * @author Lenovo
 * @date 2024/3/16
 * @time 16:10
 * @project Mybatis-study
 **/
public class TetsDao {


    SqlSession sqlSession;
    @Test
    public void test(){
        try {
            sqlSession = MybatisUtils.getSqlSession();
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            List<User> userList = mapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭sqlSession
            sqlSession.close();
        }

    }
}

(四) 总结:易错点:

mapper文件中:namespace 中的名字要和接口的路径地址和名字一致

mybatis-config文件中:

Pom.xml 文件中:maven的资源过滤要加上

    <!--    maven资源过滤-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

3.CRUD

(一) 查

    List<User> getUserList();

    User getUserById(int id);

<!--    查询语句:resultType返回为User-->
    <select id="getUserList" resultType="com.jiang.pojo.User">
    select * from mybatis.user;
  </select>
    
<!--    根据id查询用户-->
    <select id="getUserById"  resultType="com.jiang.pojo.User" parameterType="int">
    select * from mybatis.user where id=#{id};
  </select>

id:表示sql语句操作方法的名字;

resultType: 表示返回的类型

parameter: 表示查询参数的类型

 SqlSession sqlSession;
    @Test
    public void test(){
        try {
            sqlSession = MybatisUtils.getSqlSession();
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            List<User> userList = mapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }

    }

    @Test
    public void getUserById(){
        sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        User resultUser = mapper.getUserById(2);
        System.out.println(resultUser);
        sqlSession.close();
    }

(二) 增
 //增
    int addUser(User user);

    <insert id="addUser" parameterType="com.jiang.pojo.User">
        insert into mybatis.user (id,name,pwd) value (#{id},#{name},#{pwd})
    </insert>

 @Test
    public void addUser(){
        sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        int resultNumber = mapper.addUser(new User(4, "哈哈", "1254adasd"));

        if (resultNumber > 0){
            System.out.println("插入成功");
        }

        //增删改都需要提交事务:
        sqlSession.commit();
        sqlSession.close();
    }
(三) 删

   //删
    int deleteUser(int id); //根据id 删除对象

删除操作:

    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id=#{id}
    </delete>

    @Test
    public void deleteUser(){
        sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);

        int i = mapper.deleteUser(4);

        if (i >0){
            System.out.println("删除操作成功");
        }

        //增删改都需要提交事务:
        sqlSession.commit();
        sqlSession.close();
    }

(四) 改
   //改
    int updateUser(User user); //传入一个User对象

    <update id="updateUser" parameterType="com.jiang.pojo.User">
        update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
    </update>

    @Test
    public void updateUser(){
        sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        int resultNumber= mapper.updateUser(new User(4,"呵呵","454dsdsf"));

        if (resultNumber > 0){
            System.out.println("更新成功");
        }
        //增删改都需要提交事务:
        sqlSession.commit();
        sqlSession.close();
    }

4.Map传入参数

Map:

如果实体类或者数据库中表,字段,参数过多可以使用map集合。

    int addUser02(Map<String,Object> map);


//参数类型是map的类型
<insert id="addUser02" parameterType="map">
        insert into mybatis.user (id,name,pwd) value (#{userId},#{userName},#{userPwd})
    </insert>

    @Test
    public void addUser02(){
        sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);

        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("userId","5");
        map.put("userName","hehe");
        map.put("userPwd","asfsd132123");
        int i = mapper.addUser02(map);
        if (i>0){
            System.out.println("添加一个用户成功");
        }

        //增删改都需要提交事务:
        sqlSession.commit();
        sqlSession.close();
    }
模糊查询:
  1. java代码传入通配符:%xxx%
  2. 在sql中拼接通配符 % xxx %

5.配置解析

mybatis核心配置文件:

(一) 环境配置:environments

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

可以配置多个环境:


 

        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSl=trur&amp;sueUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>

mybatis默认使用的事务管理器是:JDBC, 默认使用的数据库连接池是POOLED

(二) 属性:Properties

这些属性可以在外部进行配置,并可以进行动态替换。

进行数据库连接的属性配置:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSl=true;sueUnicode=true;characterEncoding=UTF-8;serverTimezone=Asia/Shanghai
username=root
password=root

在mybatis-config文件中替换掉数据库的配置使用${xxx} 来进行替换。

   <properties resource="db.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>

注意:

因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之最低优先级的则是 properties 元素中指定的属性

(三) 设置:settings

设置日志

(四) 类型别名:typeAliases
  1. 给每一个实体类起别名 (在mybatis-config.xml中加入)
<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
  1. 扫描包 (扫描一个包下面所有的类,java 会自动寻找Bean)
<typeAliases>
  <package name="domain.blog"/>
</typeAliases>
  1. 使用注解的方式

在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值

@Alias("author")
public class Author {
    ...
}

其他配置了解。

6.映射器(mappers)

作用:告诉 MyBatis 到哪里去找映射文件。

1.根据路径:(推荐使用)
<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

2.根据class文件去绑定:

注意:但是接口 Dao 和它的Mapper文件要在同一个包下,而且要同名。

<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

3.通过包来去绑定:

注意:但是接口和它的Mapper文件要在同一个包下,而且要同名。

<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

7.作用域和生命周期:

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

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 (一旦创建就一直存在)。

可以理解为:数据库连接池,一直存在,直到程序结束。

SqlSession

个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。

每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。

  1. 连接到连接池的一个请求。
  2. SqlSession 是线程不安全的,因此不可以被共享,所以最佳作用域是请求或者方法作用域。
  3. 用完之后需要关闭,否则资源被占用。

8.ResultMap(结果集映射)

一、解决实体类属性名和数据库字段名不一致的问题

User类中的password, 而数据库中的字段名是:pwd。

解决:

  1. 起别名
  2. 结果集映射:ResultMap

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。

使用resultMap将实体类的属性名和数据库中的字段不一致解决掉:

    <resultMap id="userResultMap" type="User">
        <result property="userName" column="name"/>
        <result property="password" column="pwd"/>
    </resultMap>

    <select id="getUserList" resultMap="userResultMap">
    select * from mybatis.user;
    </select>

9.日志工厂

如果一个数据库操作,出现了异常,就需要排错,这时就可以使用日志工厂。

mybatis可以使用的日志:

SLF4J

LOG4J (需要手动配置一些东西才可以使用)

LOG4J2

JDK_LOGGING

COMMONS_LOGGING

STDOUT_LOGGING (mybatis默认使用的,不需要任何的配置)

设置日志:

使用STDOUT_LOGGING:

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

结果:

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1433666880.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55740540]
==>  Preparing: select * from mybatis.user; 
==> Parameters: 
<==    Columns: id, name, pwd
<==        Row: 1, 狂神, 123456
<==        Row: 2, 张三, abcdef
<==        Row: 3, 李四, 987654
<==        Row: 5, hehe, asfsd132123
<==      Total: 4
User{id=1, userName='狂神', password='123456'}
User{id=2, userName='张三', password='abcdef'}
User{id=3, userName='李四', password='987654'}
User{id=5, userName='hehe', password='asfsd132123'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55740540]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55740540]
Returned connection 1433666880 to pool.

使用LOG4j日志:

  1. mybatis-config.xml中配置好LOG4j
   <settings>
        <setting name="logImpl" value="LOG4j"/>
    </settings>
  1. Pom.xml中导入LOG4j的包的依赖

https://mvnrepository.com/artifact/org.apache.logging.log4j

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

  1. resource目录下:配置一个log4j.properties文件
# priority  :debug<info<warn<error
#you cannot specify every priority with different file for log4j 
log4j.rootLogger=debug,stdout,info,debug,warn,error
#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n
#info log
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
#存储在当前目录下的文件
log4j.appender.info.File=./src/log/info.log 
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout 
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#debug log
log4j.logger.debug=debug
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.debug.File=./src/log/debug.log
log4j.appender.debug.Append=true
log4j.appender.debug.Threshold=DEBUG
log4j.appender.debug.layout=org.apache.log4j.PatternLayout 
log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#warn log
log4j.logger.warn=warn
log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.warn.File=./src/log/warn.log
log4j.appender.warn.Append=true
log4j.appender.warn.Threshold=WARN
log4j.appender.warn.layout=org.apache.log4j.PatternLayout 
log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#error
log4j.logger.error=error
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.error.File = ./src/log/error.log 
log4j.appender.error.Append = true
log4j.appender.error.Threshold = ERROR 
log4j.appender.error.layout = org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

4.写代码:

10.分页

实现limit分页: sql语句的用法

SELECT * FROM USER LIMIT startIndex, pageSize;

<resultMap id="userResultMap" type="User">
    <result property="userName" column="name"/>
    <result property="password" column="pwd"/>
</resultMap>

<select id="getUserByLimit"  parameterType="map" resultMap="userResultMap">
    select * from mybatis.user limit #{startIndex},#{pagaSize};
</select>

    @Test
    public void getUserByLimit(){

        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        //使用map
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("startIndex",0);
        map.put("pagaSize",2);

        List<User> userList= mapper.getUserByLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }

        sqlSession.close();
    }

11.使用注解开发:用来替代Mapper.xml

不推荐使用:还是建议使用mapper.xml的方式。

底层是反射机制和动态代理,不用使用Mapper文件;但是只能写简单的sql语句

@param("xxx") 以param中的参数名为查询字段;引用类型不需要加,只有一个基本类型的话也可以不加,但是建议加上。

package com.keafmd.dao;

import com.keafmd.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

/**
 * Keafmd
 *
 * @ClassName: IUserDao
 * @Description:
 * @author: 牛哄哄的柯南
 * @date: 2021-02-16 20:30
 */

/**
 * 在mybatis中针对CRUD一共有四个注解
 * @Select @Insert @Update @Delete
 */
public interface IUserDao {

    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    List<User> findAll();

    /**
     * 保存用户
     * @param user
     */
    @Insert("insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})")
    void saveUser(User user);

    /**
     * 更新用户
     * @param user
     */
    @Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
    void updateUser(User user);

    /**
     * 删除用户
     * @param userId
     */
    @Delete("delete from user where id=#{id}")
    void deleteUser(Integer userId);

    /**
     * 根据id查询用户
     * @param userId
     * @return
     */
    @Select("select * from user where id=#{id}")
    User findById(Integer userId);

    /**
     * 根据用户名称模糊查询
     * @param username
     * @return
     */
    //@Select("select * from user where username like #{username}") //占位符
    @Select("select * from user where username like '%${value}%'")  //字符串拼接
    List<User> findByName(String username);

    /**
     * 查询总数量
     * @return
     */
    @Select("select count(*) from user")
    int findTotal();

}

自动提交事务:

12.Lombok的使用:

  1. 第一步安装lombok的插件
  2. 第二步导入lombok的依赖

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>

常用的注解:

@Data

@Data最常用的注解之一。注解在类上,提供该类所有属性的getter/setter方法,还提供了equals、canEqual、hashCode、toString方法。

@Log4j

作用于类上,为该类提供一个属性名为log的log4j日志对象。

@AllArgsConstructor

作用于类上,为该类提供一个包含全部参的构造方法,注意此时默认构造方法不会提供。

@NoArgsConstructor

作用于类上,提供一个无参的构造方法。可以和@AllArgsConstructor同时使用,此时会生成两个构造方法:无参构造方法和全参构造方法。

不建议使用

13.复杂操作:

结果映射(resultMap)
  • constructor - 用于在实例化类时,注入结果到构造方法中
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association – 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection – 一个复杂类型的集合
    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator – 使用结果值来决定使用哪个 resultMap
    • case – 基于某些值的结果映射
      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

id和result的属性:

属性

描述

property

映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。

column

数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。

javaType

一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。

jdbcType

JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。

typeHandler

我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。

ofType: 指代泛型的类型;

关联:association ;关联查询的时候

<resultMap id="blogResult" type="Blog">
  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectAuthor" resultType="Author">
  SELECT * FROM AUTHOR WHERE ID = #{id}
</select>

(一) 多对一的关系:

例子:多个学生对一个老师

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核心配置文件-->
<configuration>
<!--    配置数据库连接-->
    <properties resource="db.properties"/>
<!--    设置日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

<!--    起别名:-->
    <typeAliases>
        <typeAlias type="com.jiang.pojo.Teacher" alias="Teacher"/>
        <typeAlias type="com.jiang.pojo.Student" alias="Student"/>
    </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>

        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSl=trur&amp;sueUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>

    </environments>

<!--    注册每一个mapper文件-->
    <mappers>
       <mapper class="com.jiang.dao.studentMapper"/>
        <mapper class="com.jiang.dao.teacherMapper"/>
    </mappers>

</configuration>

package com.jiang.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author Lenovo
 * @date 2024/3/20
 * @time 15:22
 * @project Mybatis-study
 **/

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private int id;
    private String name;
    private Teacher teacher;

}




package com.jiang.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author Lenovo
 * @date 2024/3/20
 * @time 15:22
 * @project Mybatis-study
 **/

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {

    private int id;
    private String name;
}

package com.jiang.dao;

import com.jiang.pojo.Student;
import java.util.List;

/**
 * @author Lenovo
 * @date 2024/3/20
 * @time 15:13
 * @project Mybatis-study
 **/
public interface studentMapper {

    //获取所有学生及对应老师的信息
    List<Student> getStudents();
}

查询嵌套处理: 按照查询进行嵌套处理就像SQL中的子查询

<?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.jiang.dao.studentMapper">

    <!--
        需求:获取所有学生及对应老师的信息
    思路:
    1. 获取所有学生的信息
    2. 根据获取的学生信息的老师ID->获取该老师的信息
    3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询?
        1. 做一个结果集映射:StudentTeacher
        2. StudentTeacher结果集的类型为 Student
        3. 学生中老师的属性为teacher,对应数据库中为tid。
           多个 [1,...)学生关联一个老师=> 一对一,一对多
        4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
-->
    <select id="getStudents" resultMap="TeacherStudents">
        select  * from student
    </select>
    <resultMap id="TeacherStudents" type="Student">
        <!--association关联属性 property属性名 javaType:返回的属性类型
        column在多的一方的表中的列名
        select:表示执行的子查询操作名:
        -->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id=#{id}
    </select>

</mapper>

测试:

package com.jiang.dao;

import com.jiang.pojo.Student;
import com.jiang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

/**
 * @author Lenovo
 * @date 2024/3/20
 * @time 15:49
 * @project Mybatis-study
 **/
public class MyTest {


    @Test
    public void getStudents(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        studentMapper mapper = sqlSession.getMapper(studentMapper.class);
        List<Student> students = mapper.getStudents();
        for (Student student : students) {
            System.out.println(student);
        }
        sqlSession.close();
    }
}

按照结果嵌套处理:按照结果进行嵌套处理就像SQL中的联表查询

一次查询出所有的结果,将第二张表中的数据进行映射处理。

<!--    按结果映射-->
    <select id="getStudents2" resultMap="TeacherStudents2">
        select s.id sid,s.name sname,t.name tname from student s,
        teacher t where s.tid = t.id ;
    </select>
    <resultMap id="TeacherStudents2" type="Student">
        <id property="id" column="sid"/>
        <result property="name" column="sname"/>
        <!--关联对象property 关联对象在Student实体类中的属性-->
        <association property="teacher" javaType="Teacher">
<!--            properties: 是在实体中的名字,column是在数据库中的名字-->
            <result property="name" column="tname"/>
        </association>
    </resultMap>

    @Test
    public void getStudents2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        studentMapper mapper = sqlSession.getMapper(studentMapper.class);
        List<Student> students2 = mapper.getStudents2();

        for (Student student : students2) {
            System.out.println(student);
        }
        sqlSession.close();
    }


 

(二) 一对多的关系:

例子:一个老师对应多个学生:关联使用collection

package com.jiang.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author Lenovo
 * @date 2024/3/20
 * @time 15:22
 * @project Mybatis-study
 **/

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private int id;
    private String name;
    private int tid;

}



package com.jiang.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * @author Lenovo
 * @date 2024/3/20
 * @time 15:22
 * @project Mybatis-study
 **/

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {

    private int id;
    private String name;
    private List<Student> students;
}

按查询映射:

<!--    1.按查询映射:-->

    <select id="getTeacher" resultMap="TeacherStudents">
        select * from teacher where id=#{id}
    </select>
    <resultMap id="TeacherStudents" type="Teacher">
<!--       集合
           column是一对多的外键 , 写的是一的主键的列名,就是teacher的id -->
        <collection property="students" column="id" javaType="ArrayList" 
          ofType="Student"
                    select="getStudentByTeacherId"/>
    </resultMap>
    <select id="getStudentByTeacherId" resultType="Student">
        select * from student where tid=#{id}
    </select>

ofType :是泛型的映射。

按结果进行映射:

<!--    2.按结果进行映射-->

    <select id="getTeacher2" resultMap="TeacherStudents2">
        select s.id sid, s.name sname,t.name tname, t.id tid from student s, teacher t
        where s.tid = t.id and t.id =#{id};
    </select>
    <resultMap id="TeacherStudents2" type="Teacher">
<!--        老师的信息-->
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
<!--        collection :返回学生的信息-->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

注意:

association是用于一对一和多对一,而collection是用于一对多的关系

JavaType和ofType都是用来指定对象类型的

  • JavaType是用来指定pojo中属性的类型
  • ofType指定的是映射到list集合属性中pojo的类型。

14.动态SQL:

准备:

package com.jiang.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @author Lenovo
 * @date 2024/3/20
 * @time 18:48
 * @project Mybatis-study
 **/

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {

    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;

}





package com.jiang.dao;

import com.jiang.pojo.Blog;

import java.util.List;

/**
 * @author Lenovo
 * @date 2024/3/20
 * @time 18:49
 * @project Mybatis-study
 **/
public interface BlogMapper {
    /**
     * 增加一个blog
     * @param blog
     * @return
     */
    int addBlog(Blog blog);

    List<Blog> getBlogList();
}

package com.jiang.utils;

import java.util.UUID;

/**
 * @author Lenovo
 * @date 2024/3/20
 * @time 18:47
 * @project Mybatis-study
 **/
public class IDutils {

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

}

<mapper namespace="com.jiang.dao.BlogMapper">

    <insert id="addBlog" parameterType="Blog">
        insert into mybatis.blog (id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views})
    </insert>

    <select id="getBlogList" resultType="Blog">
        select * from blog
    </select>
</mapper>

MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

if:
<select id="findActiveBlogWithTitleLike"
  resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

    <select id="queryBlogByIf" resultType="Blog" parameterType="map">
        select * from blog where
        <if test="title !=null">
            title=#{title}
        </if>

        <if test="author !=null">
            and author=#{author}
        </if>
    </select>


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

同义替换:where会自动去除and 或者or

select * from blog
<where>
  <if test="title != null">
    title = #{title}
  </if>
  <if test="author != null">
    and author = #{author}
  </if>
</where>

    @Test
    public void queryBlogByIf(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("title","Mybatis如此简单");
        map.put("author","狂神说");
        List<Blog> blogs = mapper.queryBlogByIf(map);

        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }

trim:
  1. where: 条件判断的语句
<select id="findActiveBlogLike"
  resultType="Blog">
  SELECT * FROM BLOG
  WHERE
    <if test="state != null">
      state = #{state}
    </if>
    <if test="title != null">
      AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
      AND author_name like #{author.name}
    </if>
</select>

  1. SET: 用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列. (更新语句update时,去使用
<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

更新操作:

  int updateBlog(Map map);

<!--    更新语句-->
    <update id="updateBlog" parameterType="map">
        update blog
        <set>
            <if test="title !=null">
                title=#{title},
            </if>
            <if test="author !=null">
                author=#{author}
            </if>
        </set>
        where  id=#{id}
    </update>

    @Test
    public void updateBlog(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("title","python如此简单");
        map.put("author","小明");
        map.put("id","f7d6ccc28b3a4882adeb86726d354698");

        mapper.updateBlog(map);
        sqlSession.close();
    }
choose:

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用MyBati