MyBatis

1. 简介

1.1 什么是MyBatis?

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

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

1.2 持久化

数据持久化
持久化:将程序的数据在持久状态和瞬时状态转化的过程
为什么需要持久化?
有一些对象在内存中不能丢失,需要保存到数据库。

1.3 持久层

Dao层、Service层、Controller层…

  • 完成持久化工作的代码块
  • 层界限十分明显

1.4 为什么需要MyBatis?

  • 方便
  • 传统的JDBC代码复杂,简化
  • 帮助将数组存入到数据库中

2. 第一个MyBatis程序

思路:搭建环境->导入jar包->编写代码->测试

2.1 搭建环境

搭建数据库

create table `user`(
	`id` int(20) not null,
	`name` VARCHAR(30) DEFAULT null,
	`pwd` VARCHAR(30) default null,
	primary key(`id`)
)engine=innodb default charset=utf8

insert into `user`(id,`name`,pwd)
values(1,'张三',123456),(2,'李四',123456),(3,'王五',123456)

新建项目
1.创建一个普通Maven项目
2.导入依赖

<dependencies>
<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>
<!--mysql驱动-->
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
  </dependency>
<!--MyBatis-->
  <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.2</version>
  </dependency>
<!--junit-->
  <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
  </dependency>
</dependencies>

2.2 添加模块

  • 编写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>
    <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://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
  • 编写MyBatis工具类
public class MyBatisUtil {
    private static  SqlSessionFactory sqlSessionFactory = null;
    static {
        try {
            // 获取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();
    }
}

2.3 编写代码

  • 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}
  • Dao接口
public interface userDao {
    List<User> getUserList();
}
  • 接口实现类由原来的UserDaoImpl转变为一个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 绑定一个对应的Dao/mapper接口-->
<mapper namespace="com.hua.dao.userDao">
<!--查询语句  id对应Dao/mapper接口的方法的名字  -->
    <select id="getUserList" resultType="com.hua.pojo.User">
        select * from user
    </select>
</mapper>

2.4 测试

常见报错:

org.apache.ibatis.binding.BindingException: Type interface com.hua.dao.userDao is not known to the MapperRegistry.

解决方案:MyBatis核心配置文件中缺少以下代码

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

因为Usermapper.xml文件是放在java目录下的,Maven约定大于配置,所以会报以下的错误

Caused by: java.io.IOException: Could not find resource com/hua/dao/UserMapper.xml

解决方案:
在pom.xml中添加

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

若出现以下错误:实在xml文件中使用中文注释,删除即可

Caused by: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效。

  • junit测试
public class UserDaoTest {
    @Test
    public void test() {
        // 1. 获得sqlSession对象
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        // 2. 方式一 getMapper
        userDao userDao = sqlSession.getMapper(userDao.class);
        List<User> userList = userDao.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        // 3.关闭 sqlSession
        sqlSession.close();
    }
}

3. 增删改查

3.1 namespace

namespace中的包名要和Dao/mapper接口的包名一致

3.2 Select

id:对应namespace中的方法名
resultType:sql语句执行的返回值
parameterType:参数类型
UserMapper:

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

UserMapper.xml

<select id="getUserByID" resultType="com.hua.pojo.User" parameterType="int">    
   select * from user where id = #{id}
</select>

test

public void test1(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserByID(3);
    System.out.println(user);
    sqlSession.close();
}

3.3 Insert

与之前不同的是,增删改必须要写提交事务

UserMapper

// 增加一个用户int
addUser(User user);

UserMapper.xml

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

test

public void add(){
   SqlSession sqlSession = MyBatisUtil.getSqlSession();
   UserMapper mapper = sqlSession.getMapper(UserMapper.class);
   User user = new User(4, "赵六", "123456");
   int i = mapper.addUser(user);
   sqlSession.commit();
   System.out.println(user);
   if(i>0){
       System.out.println("添加成功");
   }else{
       System.out.println("添加失败");
   }
   sqlSession.close();
}

3.4 Update

提交事务

UserMapper

// 修改用户
int updateUser(User user);

UserMapper.xml

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

test

public void update(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User(1,"陈一","987654");
    int i = mapper.updateUser(user);
    sqlSession.commit();
    if(i>0){
        System.out.println("修改成功");
    }else{
        System.out.println("修改失败");
    }
    sqlSession.close();
}

3.5 Delete

提交事务

UserMapper

// 删除用户
int deleteUser(int id);

UserMapper.xml

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

test

public void delete(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int i = mapper.deleteUser(3);
    sqlSession.commit();
    if(i>0){
        System.out.println("删除成功");
    }else{
        System.out.println("删除失败");
    }
     sqlSession.close();
}

3.6 错误点

1.在配置mapper.xml时路径是由/表示

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

2.标签匹配错误 insert select update delete

3.7 万能Map

如果数据库中表的字段太多,考虑使用Map
使用Map比较灵活,如果只有一个参数,在sql中可以直接用接口中函数中的参数;如果是一个对象,在sql中可以直接写对象的属性;如果都不是,可以使用map,这样在sql语句中,就可以直接使用map的key

// map增加用户
int add(Map<String,Object> map);
<insert id="add" parameterType="map">    
   insert into user(id,name,pwd) values(#{userID},#{userName},#{userPwd})
</insert>
public void addMap(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String, Object> map = new HashMap<>();
    map.put("userID",6);
    map.put("userName","田七");
    map.put("userPwd","123");
    int i = mapper.add(map);
    sqlSession.commit();
    if(i>0){
        System.out.println("添加成功");
    }else{
        System.out.println("添加失败");
    }
    sqlSession.close();
}

3.8 模糊查询

List<User> getUser(String name);
<select id="getUser" resultType="com.hua.pojo.User">    
   select * from user where name like #{name}
</select>
public void selectUser(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> user = mapper.getUser("%李%");
    for (User user1 : user) {
        System.out.println(user1);
    }
}
select * from user where name like "%"#{name}"%"
List<User> user = mapper.getUser("李");

4. 配置解析

4.1 核心配置文件

mybatis-config.xml
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

4.2 环境配置(environments)

MyBatis 可以配置成适应多种环境
尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
MyBatis默认的事务管理器就是JDBC(MANAGED),连接池:POOLED(UNPOOLED|JNDI)

4.3 属性(properties)

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

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

在核心配置文件中引入 properties在xml中写在最上边 在xml文件中各种类型是由顺序的

<configuration>
    <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>
    </environments>
    
    <mappers>
        <mapper resource="com/hua/dao/UserMapper.xml"/>
    </mappers>
</configuration>

另外也可以在外部配置文件中写一部分,再在properties中写一部分,如果两者都有同一字段,外部配置文件优先级高

<configuration>
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </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>
    <mappers>
        <mapper resource="com/hua/dao/UserMapper.xml"/>
    </mappers>
</configuration>

4.4 类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
在MyBatis-config.xml文件中配置
第一种方式 具体到类

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

第二种方式 具体到包

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

使用场景:如果实体类较少,使用第一种方式,如果实体类较多,使用第二种方式
第一种方式可以自定义别名,第二种方式之前默认小写,现在大小写都可以,如果非想使用别名,可以在类中使用注解

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

下面是一些常见的 Java 类型内建的类型别名。它们都是不区分大小写的。注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
基本数据类型前边加下划线,其余变为小写

别名			映射的类型
_byte	  	byte
_long	  	long
_short	  	short
_int	 	int
_integer	int
_double		double
_float		float
_boolean	boolean
string		String
byte		Byte
long		Long
short		Short
int			Integer
integer		Integer
double		Double
float		Float
boolean	    Boolean
date	    Date
decimal	    BigDecimal
bigdecimal	BigDecimal
object		Object
map			Map
hashmap		HashMap
list		List
arraylist	ArrayList
collection	Collection
iterator	Iterator

4.5 设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
在这里插入图片描述
在这里插入图片描述
驼峰命名问题,数据库中 user_name,实体类 userName

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

4.6 映射器(mappers)

方式一:(推荐)

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

方式二:
该种方式有要求:mapper.xml文件和mapper接口必须同包同名

<mappers>
   <mapper class="com.hua.dao.UserMapper"></mapper>
</mappers>

方式三:
该种方式有要求:mapper.xml文件和mapper接口必须同包同名

<mappers>
   <package name="com.hua.dao"/>
</mappers>

4.7 生命周期和作用域

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

SqlSessionFactoryBuilder:一旦创建了 SqlSessionFactory,就不再需要它了 (局部变量)

SqlSessionFactory :一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。可以想象为数据库连接池 (全局变量)

SqlSession:连接到数据库连接池的一个请求,最佳作用域是请求或者方法作用域,用完之后关闭
在这里插入图片描述
其中每一个Mapper代表一个具体业务

5. ResultMap

解决属性名和字段名不一致的问题
数据库的字段为 id name pwd
实体类中的字段为 id name password
插入的时候不需要进行映射

insert into user(uname,uage) values (#{name},#{age})  <!--数据库中是uname,实体类是name-->

测试出现:
在这里插入图片描述
解决方案:

  • sql语句改别名
  • ResultMap 结果集映射
<mapper namespace="com.hua.dao.UserMapper">

    <resultMap id="UserMap" type="User">
        <!--column 是数据库中的字段名  property是实体类中的属性名-->
        <result column="id" property="id"></result>
        <result column="name" property="name"></result>
        <result column="pwd" property="password"></result>
    </resultMap>
    
    <select id="getUserByID" resultMap="UserMap">
        select * from user where id = #{id}
    </select>
    
</mapper>

6. 日志

6.1 日志工厂

如果数据库操作出现异常,需要排错,用到日志
在这里插入图片描述

在MyBatis中具体使用哪一个日志实现,在设置中设定

STDOUT_LOGGING标准日志输出

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

在这里插入图片描述

6.2 Log4J

  • 可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
  • 可以控制每一条日志的输出格式;
  • 定义每一条日志信息的级别,能够更加细致地控制日志的生成过程。
  • 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

1.导入LOG4J的包

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

2.log4j.properties 直接放在resource目录下

log4j.rootLogger=DEBUG,console,file


# 控制台(console)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

# 日志文件(logFile)
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/hua.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

3.配置log4j

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

4.测试运行
在这里插入图片描述

log4j的简单使用
1.在要使用log4j的类中,导入包 import org.apache.log4j.Logger;
2.创建日志对象,参数为当前类的class static Logger logger = Logger.getLogger(TestUser.class);
3.日志级别

@Test
public void test(){
    logger.info("info:进入了test方法");
    logger.debug("debug:进入了test方法");
    logger.error("error:进入了error方法");
}

日志文件中会显示:
在这里插入图片描述

7. 分页

减少数据的处理量

7.1 limit分页

select * from user limit 05

使用MyBatis实现分页
1.UserMappper

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

2.UserMapper.xml

<select id="getUser" parameterType="map" resultMap="UserMap">    
   select * from user limit #{pageNo},#{pageSize}
</select>

3.test

public void test(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> hashMap = new HashMap<>();
    hashMap.put("pageNo",0);
    hashMap.put("pageSize",3);
    List<User> users = mapper.getUser(hashMap);
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

7.2 RowBounds分页(了解)

UserMapper
List getUserByRowBounds();
UserMapper.xml

<select id="getUserByRowBounds"  resultMap="UserMap">
   select * from user
</select>

test

@Test
public void getUserByRowBounds(){
    RowBounds rowBounds = new RowBounds(0,3);
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    List<User> users = sqlSession.selectList("com.hua.dao.UserMapper.getUserByRowBounds", null, rowBounds);
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

7.3 分页插件

在这里插入图片描述

8. 注解开发

8.1 面向接口编程

  • 大家学过面向对象编程,也学习过接口,但在真正的开发中,很多时候会选择面向接口编程
  • 根本原因: 解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
  • 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲不重要;
  • 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

关于接口的理解:

  • 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
  • 接口的本身反映了系统设计人员对系统的抽象理解。
  • 接口应有两类:
  • 第一类是对一个体的抽象,它可对应为一个抽象体(abstract class);
  • 第二类是对一个体某一方面的抽象,即形成一个抽象面(interface) ;
  • 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。

三个面向对象区别

  • 面向对象是指,考虑问题时,以对象为单位,考虑它的属性及方法
  • 面向过程是指,考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构

8.2 使用注解开发

本质:反射机制实现

UserMapper 注解在接口上实现

public interface UserMapper {
    @Select("select  * from user")
    List<User> getUser();
}

配置 mybatis-congfig.xml

<mappers>
   <mapper class="com.hua.dao.UserMapper"></mapper>
</mappers>

test

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> users = mapper.getUser();
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

MyBatis详细执行流程
在这里插入图片描述

8.3 CRUD

使用注解写sql语句的前提是在MyBatis配置文件中的mappers中配置UserMapper
在MyBatis工具类中可以实现自动提交事务

public static SqlSession getSqlSession(){
   return sqlSessionFactory.openSession(true);
}

方法存在多个参数,所有参数的前面必须加上@Param(“id”) @Param(“name”) 在sql语句中取值是取的@Param中的值

1.查询
UserMapper

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

test

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserByID(2);
    System.out.println(user);
    sqlSession.close();
}

2.增加
UserMapper

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

test

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User(20,"小一","566");
    mapper.addUser(user);
    sqlSession.close();
}

3.删除
UserMapper

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

test

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int i = mapper.deleteUser(20);
    System.out.println(i);
    sqlSession.close();
}

4.修改
UserMapper

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

test

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User(20,"小五","120");
    int i = mapper.updateUser(user);
    System.out.println(i);
}

关于@Param注解

  • 基本类型或者String类型,都要加上@Param
  • 如果是引用数据类型不需要加
  • 如果只有一个基本类型或者String类型的话,建议加上
  • 在sql语句中取得值正是从@Param(“uid”)中设置的属性名

9. Lombok

取舍使用

使用步骤:

  • 在IDEA中安装Lombok插件
  • 在项目中导入jar包
  • 在类或者方法名之上添加注解
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.18.24</version>
</dependency>

@Data:生成 无参构造、get set方法、toString、hashCode、Equals
@AllArgsConstructor:有参构造
@NoArgsConstructor:无参构造
@Getter:get方法
@Setter:set方法
@EqualsAndHashCode:hashCode、Equals方法
@ToString:toString

10. 多对一

多个学生,一个老师

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

创建测试数据库表

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');

student表

idnametid
1小明1
2小红1
3小张1
4小李1
5小王1
teacher表
idname
1老秦

测试环境搭建
1.导入lombok

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>

2.新建实体类 Teacher Student

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private int id;
    private String name;
    private Teacher teacher; // 多个学生对应一个老师
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
}

3.建立Mapper接口
4.建立Mapper.xml文件
在这里插入图片描述

5.在配置文件中注册Mapper接口(sql语句用注解)或者.xml文件

<mappers>
   <mapper resource="com/hua/dao/TeacherMapper.xml"></mapper>
   <mapper resource="com/hua/dao/StudentMapper.xml"></mapper>
</mappers>

6.测试

按照查询嵌套处理

<mapper namespace="com.hua.dao.StudentMapper">

    <resultMap id="studentTeacher" type="student">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <association property="teacher" column="tid" javaType="teacher" select="getTeacher"></association>
    </resultMap>

    <select id="getStudent"  resultType="studentTeacher">
         select * from student
    </select>
    
    <select id="getTeacher" resultType="teacher">
        select * from teacher where id = #{tid} <!--这边的#{}的内容写啥都行 一般与上方对应-->
    </select>
    
</mapper>

按照结果嵌套处理

<mapper namespace="com.hua.dao.StudentMapper">

    <resultMap id="studentTeacher" type="student">
        <result  property="id" column="sid"></result>
        <result  property="name" column="sname"></result>
        <association property="teacher" javaType="teacher">
            <result property="name" column="tname"></result>
        </association>
    </resultMap>

    <select id="getStudent" resultMap="studentTeacher">
       select s.id sid,s.name sname,t.name tname
       from student s,teacher t
       where s.tid = t.id
    </select>

</mapper>

11. 一对多

student

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private int id;
    private String name;
    private int tid;
}

teacher

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

按查询嵌套处理

<mapper namespace="com.hua.dao.TeacherMapper">
    <resultMap id="st" type="teacher">
        <collection property="students" column="id" javaType="ArrayList"  ofType="student" select="getStudent"></collection>
    </resultMap>

    <select id="getTeacher" resultMap="st">
        select * from teacher where id = #{tid}
    </select>

    <select id="getStudent" resultType="student">
        select  * from student where tid = #{tid}
    </select>
</mapper>

按结果嵌套处理

<mapper namespace="com.hua.dao.TeacherMapper">
    
    <select id="getTeacher" resultMap="st">
        select s.id sid,s.`name` sname,t.id tid,t.`name` tname
        from student s,teacher t
        where s.tid = t.id and t.id = #{tid}
    </select>

    <resultMap id="st" type="teacher">
        <result property="id" column="tid"></result>
        <result property="name" column="tname"></result>
        <collection property="students" ofType="student">
            <result property="id" column="sid"></result>
            <result property="name" column="sname"></result>
        </collection>
    </resultMap>
</mapper>

小结:
关联:assocation 多对一
集合:collection 一对多

javaType:用来指定实体类中属性的类型
ofType:用来指定泛型类型

12. 动态SQL

根据不同的条件生成不同的sql语句

搭建环境

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

创建一个基础工程
1.导包
2.编写配置文件
3.编写实体类 工具类
4.编写实体类对应的Mapper文件
5.编写接口文件对应的Mapper.xml文件

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

添加了一个可以生成随机id的工具类

public class IDUtils {
    public static String getId(){
        return UUID.randomUUID().toString().replace("-","");
    }
    @Test
    public void test() {
        System.out.println(IDUtils.getId());
    }
}

可以解决数据库中的下划线与实体类中的驼峰命名不一致问题

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

12.1 IF

List<Blog> queryBlogIf(Map<String,Object> map);

方式一

<select id="queryBlog" parameterType="map" resultType="blog">
     select * from blog where 1=1
     <if test="title!=null">
            and title = #{title}
     </if>
     <if test="author!=null">
            and author = #{author}
     </if>
</select>
@Test
public void queryBlog(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap<String, Object> hashMap = new HashMap<>();
    hashMap.put("author","小张");
    List<Blog> blogs = mapper.queryBlogIf(hashMap);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

方式二

<select id="queryBlogIf" parameterType="map" resultType="blog">
   select * from blog
    <where>
      <if test="title!=null">
             title = #{title}
      </if>
      <if test="author!=null">
            and author = #{author}
      </if>
    </where>
</select>
@Test
public void queryBlog(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap<String, Object> hashMap = new HashMap<>();
    hashMap.put("author","小张");
    List<Blog> blogs = mapper.queryBlogIf(hashMap);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

12.2 choose(when、otherwise)

<select id="queryBlogChoose" resultType="blog" parameterType="map">
   select * from blog
   <where>
     <choose>
        <when test="title!=null"> <!--只走其中一个-->
             title = #{title}
        </when>
        <when test="author!=null">
             author = #{author}
        </when>
        <otherwise>
             views = #{views}
        </otherwise>
      </choose>
  </where>
</select>

12.3 trim(where、set)

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

自定义 trim 元素来定制where元素和set元素的功能。

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)

<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 queryBlog(){
   SqlSession sqlSession = MyBatisUtils.getSqlSession();
   BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
   HashMap hashMap = new HashMap<>();
   hashMap.put("id","230e72eb677a4cb29286e3cad5dfb6be");
   hashMap.put("title","真好");
   int i = mapper.updateBlog(hashMap);
   if(i>0){
      System.out.println("修改成功");
   }else{
      System.out.println("修改失败");
   }
   sqlSession.close();
}

12.4 SQL片段

将公共部分抽取出来,进行复用

<!--使用sql标签抽取公共部分-->
<sql id="if-title-author">
     <if test="title!=null">
         and title = #{title}
     </if>
     <if test="author!=null">
         and author = #{author}
     </if>
</sql>
<!--使用include标签引用公共部分-->
<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from blog where 1=1
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

注意:

  • 最好基于单表定义sql片段
  • sql片段中不要存在where标签

12.5 Foreach

在这里插入图片描述

select * from user
<where>
    id in
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</where>

select * from user WHERE id in ( ? , ? , ? )

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map = new HashMap<>();
    List<Integer> list = new ArrayList<>();
    list.add(25);
    list.add(26);
    list.add(2);
    map.put("ids",list);
    mapper.getList(map);
    sqlSession.close();
}
<select id="queryBlogForeach" resultType="blog" parameterType="map">
  select * from blog
  <where>
     <foreach collection="ids" item="id" open="(" separator="or" close=")">
        id = #{id}
     </foreach>
  </where>
</select>
@Test
public void queryBlog(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap<>();
    ArrayList<Integer> arrayList = new ArrayList<>();
    arrayList.add(1);
    arrayList.add(2);
    arrayList.add(3);
    map.put("ids",arrayList);
    mapper.queryBlogForeach(map);
}

在这里插入图片描述

13. 缓存

13.1 简介

1.什么是缓存(Cache )?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2.为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3.什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

13.2 Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
    • 默认情况下,只有一级缓存开启。 (SqlSession级别的缓存, 也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

13.3 一级缓存

  • 一级缓存也叫本地缓存:
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

测试
1.开启日志
2.测试在一个sqlSession中查询两次相同的记录
3.查看日志输出
在这里插入图片描述

缓存失效情况:

  • 1.查询不同的东西
  • 2.增删改操作,可能会改变原来的数据,所以必定刷新缓存
@Test
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserById(1);
    System.out.println(user);
    System.out.println("---------------------");

    User user2 = new User(2, "张三", "123");
    mapper.updateUser(user2);

    User user1 = mapper.getUserById(1);
    System.out.println(user1);
    System.out.println(user==user1);
    sqlSession.close();
}

在这里插入图片描述

  • 3.查询不同的mapper
  • 4.手动清理缓存
@Test
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserById(1);
    System.out.println(user);
    System.out.println("---------------------");
    sqlSession.clearCache();  // 手动清理缓存
    User user1 = mapper.getUserById(1);
    System.out.println(user1);
    System.out.println(user==user1);
    sqlSession.close();
}

在这里插入图片描述
小结:
一级缓存默认开启的,只在一次sqlSession中有效

13.4 二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个命名空间,对应一个二级缓存;
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

测试
1.开启全局缓存 在mybatis-config.xml文件中配置

<setting name="cacheEnabled" value="true"/>

2.开启二级缓存 在Mapper.xml中

 <cache />

也可以自定义参数
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

 <cache
      eviction="FIFO"
      flushInterval="60000"
      size="512"
      readOnly="true"/>

3.测试

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserById(1);
    System.out.println(user);
    sqlSession.close();

    SqlSession sqlSession1 = MyBatisUtils.getSqlSession();
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    User user1 = mapper1.getUserById(1);
    System.out.println(user1);
    sqlSession1.close();
}

在这里插入图片描述
如果只是在Mapper.xml中只配置 <cache/>,配置其他参数,会报Caused by: java.io.NotSerializableException: com.hua.pojo.User 实体类没有序列化

小结:

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

13.5 缓存原理

在这里插入图片描述


版权声明:本文为weixin_44960905原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。