MyBatis个人学习笔记--超级详细

目录

1.MyBatis

1.1 什么是MyBatis

1.2 持久化

1.3 持久层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzLJMgk0-1606466380923)(C:\Users\AppData\Roaming\Typora\typora-user-images\image-20201125091356575.png)]

1.4 为什么需要mybatis

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ASv7ui8i-1606466380925)(C:\Users\AppData\Roaming\Typora\typora-user-images\image-20201125091703509.png)]

2. 环境搭建

2.1 数据库搭建

CREATE TABLE `user`(
	`id` INT(20) NOT NULL PRIMARY KEY,
	`name` VARCHAR(20) DEFAULT NULL,
	`pwd` VARCHAR(20) DEFAULT NULL
);
INSERT INTO USER(`id`,`name`,`pwd`) VALUES
(1,'叶辰','123456'),
(2,'若雪','123456'),
(3,'秦安','123456')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kdWSgmFj-1606466380944)(C:\Users\AppData\Roaming\Typora\typora-user-images\image-20201125092907045.png)]

2.2 新建maven项目

删除src目录,将其当做父工程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fwpTmAQe-1606466380946)(C:\Users\AppData\Roaming\Typora\typora-user-images\image-20201125093420351.png)]

2.3 导入依赖

        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>
        <!-- mybatis依赖  -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--单元测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

2.4 创建一个模块

步骤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OkbShQ6k-1606466380948)(C:\Users\AppData\Roaming\Typora\typora-user-images\image-20201125113342268.png)]

2.4.1 编写mybatis核心配置文件
XML 中构建 SqlSessionFactory
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
mybatis-config.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <!--事务管理-->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/blade?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull&amp;transformedBitIsBoolean=true&amp;serverTimezone=GMT%2B8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
2.4.2 编写mybatis工具类
package com.syp.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;

/**
 * @description:mybatis工具类
 * @return
 */

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static{
        try {
            //构建 SqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
          sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //诚然,这种方式能够正常工作,对使用旧版本 MyBatis 的用户来说也比较熟悉。
    // 但现在有了一种更简洁的方式——使用和指定语句的参数和返回值相匹配的接口
    // (比如 BlogMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。

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

2.4.3 编写代码:

实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String pwd;
}

DAO接口:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pdjzmedm-1606466380949)(C:\UsersAppData\Roaming\Typora\typora-user-images\image-20201125103112812.png)]

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

接口实现类:

<?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.syp.dao.UserDao">
    <!--select查询语句-->
    <!--id中的相当于需要重写的Mapper方法-->
    <!--resultType是返回一个结果,写Mapper中的泛型-->
    <select id="getUsersList" resultType="com.syp.pojo.User">
    select * from User where id = #{id}
  </select>
</mapper>
2.4.4 测试
    @Test
    public void getUserList(){
        //1.获得sqlserssion对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //2.执行SQL
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> usersList = userMapper.getUsersList();
        for (User user : usersList) {
            System.out.println(user);
        }
        //3.关闭sqlsession
        sqlSession.close();
    }

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uwYF405c-1606466380951)(C:\Users\AppData\Roaming\Typora\typora-user-images\image-20201125110543027.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BXofONQc-1606466380962)(C:\Users\AppData\Roaming\Typora\typora-user-images\image-20201125112416285.png)]

2.5 知识点:

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

3.CRUD

3.1 注意事项:

resources绑定mapper,需要使用路劲

namespace中要用包名:

namespace="com.syp.dao.UserMapper"
增删改需要提交事务,不然不会提交成功
3.2 参数:
1.namespace

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

2.select
id

id: id就是对应的namespace中的方法名

resultType

resultType: sql语句执行的返回值

parameterType

parameterType: 参数类型

3.3 根据id查询用户
编写接口
//根据id查询用户
    User getUserById(Integer id);
编写mapper中的SQL语句
   <select id="getUserById" parameterType="integer" resultType="com.syp.pojo.User">
        select * from User where id =  #{id} ;
    </select>
测试
 @Test
    public void getUserById(/*Integer id*/) {
        //1.获得sqlserssion对象
        sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User userById = mapper.getUserById(1);
        System.out.println(userById);

        sqlSession.close();
    }
3.4 插入用户
编写接口
  //插入用户
    int addUser(User user);
编写mapper中的SQL语句
    <insert id="addUser" parameterType="com.syp.pojo.User">
        insert  into User (id,name,pwd) values (#{id},#{name},#{pwd});
    </insert>
测试
  //增删改需要提交事务
    @Test
    public void addUser() {
        sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int result = mapper.addUser(new User(4, "好乖好", "123456"));
        if (result > 0) {
            System.out.println("成功");
        }
        //提交事务
        sqlSession.commit();

        sqlSession.close();
    }

3.5 修改用户
编写接口
    //修改用户
    int updateUserById(User user);
编写mapper中的SQL语句
    <update id="updateUserById" parameterType="com.syp.pojo.User">
        update User set name = #{name},pwd = #{pwd} where id = #{id};
    </update>
测试
   //根据id修改用户
    @Test
    public void updateUserById() {
        sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int result = mapper.updateUserById(new User(4, "修改", "123456"));
        if (result > 0) {
            System.out.println("修改成功");
        }
        sqlSession.commit();
        sqlSession.close();
    }
3.6 删除用户
编写接口
    //删除用户
    int deleteUserById(Integer id);
编写mapper中的SQL语句
    <delete id="deleteUserById" parameterType="integer">
        delete from  User where id = #{id};
    </delete>
测试
    @Test
    public void deleteUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int result = mapper.deleteUserById(4);
        if (result>0){
            System.out.println("操作成功");
        }
        sqlSession.commit();

        sqlSession.close();
    }
3.7 map实现插入用户信息

只是指定需要修改的字段,不需要全部参数都修改

接口:
    //根据需要插入数据相关的字段,map,不需要知道数据库有什么,只需要插入自己的数据
    int addUserMy(Map<String,Object> map);
mapper.xml中的SQL语句:
<!--    根据需要插入数据相关的字段,map,不需要知道数据库有什么,只需要插入自己的数据
        对象中的属性可以直接取出,只需要传递map的key
-->
    <insert id="addUserMy" parameterType="map">
        insert  into User (id,name,pwd) values (#{userId},#{userName},#{userPwd});
    </insert>
数据测试:
   //增加用户数据信息,根据需要的字段
    @Test
    public void addUserMy() {
        sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String, Object> map = new HashMap<String, Object>();

        map.put("userId", 5);
        map.put("userName", "第五个");
        map.put("userPwd", "123456");

        System.out.println("插入成功");
        mapper.addUserMy(map);

        //提交事务
        sqlSession.commit();

        sqlSession.close();
    }

3.8 map实现修改用户信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pjH8JKou-1606466380964)(C:\UsersAppData\Roaming\Typora\typora-user-images\image-20201125140240647.png)]

接口:
    //传需要修改的字段进行修改
    int updateUserMy(Map<String,Object> map);
mapper.xml中的SQL语句:
    <update id="updateUserMy" parameterType="map">
        update user set name = #{userName} where id=#{userId};
    </update>

数据测试:
 //根据id修改用户名修改用户,通过map
    @Test
    public void updateUserMy() {
        sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("userId", 5);
        map.put("userName", "修改的测试5");


        System.out.println("修改成功");
        mapper.updateUserMy(map);
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }
3.9 模糊查询
接口
    //模糊查询一个用户数据
    List<User> getUserLike(String str);
mapper.xml中的SQL语句
    <select id="getUserLike" parameterType="string" resultType="com.syp.pojo.User">
        select * from user where name like #{str};
    </select>
数据测试:
    //根据姓名来模糊查询用户信息
    @Test
    public void getUserLike(){
         sqlSession = MybatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userLike = mapper.getUserLike("%秦%");
        for (User user : userLike) {
            System.out.println(user);
        }
        sqlSession.close();

    }

4.配置解析

1.核心配置文件

mybatis-config.xml

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

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

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

编写配置文件db.properies:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8
username=root
password=123456

在核心配置文件中引入:

​ properies文件中的标签都有自己的顺序,需要有自己的顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKnWTc6X-1606466380968)(C:\Users\AppData\Roaming\Typora\typora-user-images\image-20201125145111071.png)]

<!--引入外部配置文件,优先使用外部配置文件-->
<properties resource="db.properties">
    <property name="username" value="root"/>
</properties>
<dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>
3.类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

1.一个类使用一个别名:

 <!--更改别名-->
<typeAliases>
    <typeAlias type="com.sypc.pojo.User" alias="User"/>
</typeAliases>

2.一个包里面扫描使用一个别名:

​ 指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean

​ 每一个在包中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。若有注解,则别名为其注解值。

@Alias("user")
4.设置
5.映射器

定义 SQL 映射语句。 需要告诉 MyBatis 到哪里去找到这些语句。

​ 在自动查找资源方面,Java 并没有提供一个很好的解决方案,最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或类名和包名等。

方式一:完全限定类名(推荐)

​ 每一个mapper.xml文件都需要在mybatis核心配置文件中注册

<!-- 使用映射器接口实现类的完全限定类名 -->
<!-- 每一个mapper.xml文件都需要在mybatis核心配置文件中注册 -->
<!-- 使用相对于类路径的资源引用 -->
  <mappers>
        <mapper resource="com/sypc/dao/UserMapper.xml"></mapper>
    </mappers>
方式二:使用class绑定注册
   <mappers>
        <!-- 使用映射器接口实现类的完全限定类名 -->
        <mapper class="com.sypc.dao.UserMapper"></mapper>
    </mappers>

注意点:接口和mapper文件必须同名,而且要在一个包下

方式三:使用扫描包进行绑定
<!-- 将包内的映射器接口实现全部注册为映射器 -->
    <mappers> 
        <!-- 将包内的映射器接口实现全部注册为映射器 -->
        <package name="com.sypc.dao"/>
    </mappers>
知识点:

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

  • MANAGED – 这个配置几乎没做什么。从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。

    <transactionManager type="MANAGED">
      <property name="closeConnection" value="false"/>
    </transactionManager>
    
5.作用域(Scope)和生命周期

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

SqlSessionFactoryBuilder
  • 一旦创建了 SqlSessionFactory,就不再需要它了。

  • 局部变量

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

  • 应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。

  • SqlSessionFactory 的最佳作用域是应用作用域

  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession
  • 连接到数据库连接池的一个请求

  • SqlSession 的实例不是线程安全的,因此是不能被共享的

  • 最佳的作用域是请求或方法作用域。

  • 用完后需要关闭,防止资源被一直占用。

5.属性名和字段名不一致的解决

数据库字段:

java实体类:

/**
 * @description:用户实体类
 * @return
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String password;
}

解决方法:

- 起别名
    <select id="getUsersList" resultType="com.syps.pojo.User">
		select id,name,pwd as password from  user;
    </select>
- resultMap结果集映射

在mapper.xml中进行相关的映射:

  • resultMap 元素是 MyBatis 中最重要最强大的元素。可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • ResultMap 的优秀之处——可以不用显式地配置它们,只需要配置数据库字段名和实体类字段名不一致的即可

6.日志

6.1 日志工厂

日志打印

在设置中设置,即可生效

<!--配置完整的 settings-->
<settings>
    <!--日志工厂的实现-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

6.2 Log4j

1.pom文件中导入配置
<!--log4j的配置依赖-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
2.classpath下新建配置文件log4j.properties
# 文件输出位置,将日志输出到console和file这两个目的地
log4j.rootLogger=DEBUG,console,file

# 控制台输出的相关配置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

# 文件输出的相关配置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=../log/warn.log
log4j.appender.file.MaxFileSize = 5MB
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%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-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
简单实用log4j:

在要输出日志的类中加入相关语句:

定义属性:static Logger logger = Logger.getLogger(LogDemo.class); //LogDemo为相关的类

在相应的方法中:

if (logger.isDebugEnabled()){

logger.debug(“System ……”);

}

  1. 导入依赖包:
import org.apache.log4j.Logger;
  1. 生成日志对象,参数为当前日志的class

    Logger logger = Logger.getLogger(UserMapperTest.class);
    
  2. 日志级别

    logger.entry();   //trace级别的信息,单独列出来是希望你在某个方法或者程序逻辑开始的时候调用,和logger.trace("entry")基本一个意思
    logger.error("Did it again!");   //error级别的信息,参数就是你输出的信息
    logger.info("我是info信息");    //info级别的信息
    logger.debug("我是debug信息");
    logger.warn("我是warn信息");
    logger.fatal("我是fatal信息");
    logger.log(Level.DEBUG, "我是debug信息");   //这个就是制定Level类型的调用:谁闲着没事调用这个,也不一定哦!
    logger.exit();    //和entry()对应的结束方法,和logger.trace("exit");一个意思
    

7.分页

7.1limit 实现分页

select * from  `user` LIMIT startIndex, pageSize; 
select * from  `user` LIMIT 3; -- 等价于从0开始,到3结束

接口:

    //分页
    List<User> userLimitPage (Map<String ,Integer> map);

mapper.xml文件的编写:

    <resultMap id="user" type="cn.syp.entity.User">
        <!--column对应数据库中的字段,property对应实体类中的属性-->
        <result property="password" column="pwd"/>
    </resultMap>

<!-- 分页-->
    <select id="userLimitPage" parameterType="map" resultMap="user">
        select  * from user limit #{startIndex},#{pageSize};
    </select>


测试:

    //mysql分页测试
    @Test
    public void userLimitPage(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        HashMap<String, Integer> map = new HashMap<String, Integer>();

        map.put("startIndex", 1);
        map.put("pageSize", 3);
        List<User> users = mapper.userLimitPage(map);
        for (User user : users) {
            System.out.println(user);
        }
        sqlSession.close();
    }

7.2.RowBoundes实现分页(了解)

接口:

    //.RowBoundes实现分页
    List<User> userPageRowBounds();

xml文件的配置:

<!--RowBoundes实现分页-->
    <select id="userPageRowBounds" resultMap="user">
        select  * from user
    </select>

测试:

//  RowBound实现分页
    @Test
    public void userPageRowBounds(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //查询全部的数据
        //参数:方法名的地质
        List<User> users = sqlSession.selectList("cn.syp.dao.UserMapper.userPageRowBounds");
        for (User user : users) {
            System.out.println("全部用户:"+user);
        }

        //RowBound实现分页查询
        RowBounds rowBounds = new RowBounds(1,2);
        List<User> usersPage = sqlSession.selectList("cn.syp.dao.UserMapper.userPageRowBounds", null, rowBounds);
        for (User user : usersPage) {
            System.out.println("分页:"+user);
        }

    }

7.3 分页插件:

PageHelper:

官网地址:

https://pagehelper.github.io/

1. 引入分页插件

在 pom.xml 中添加如下依赖:最新版本版本号查看官网

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>
2. 配置拦截器插件

在 MyBatis 配置 xml 中配置拦截器插件:

    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!--分页参数合理化  ,默认false 时,直接根据参数进行查询。-->
<!-- 在启用合理化时,如果 pageNum<1,则会查询第一页;如果 pageNum>pages,则会查询最后一页。-->
            <property name="reasonable" value="true"/>
<!-- 默认情况下不会进行 count 查询,如果你想在分页查询时进行 count 查询, 以及使用更强大的 PageInfo 类,你需要设置该参数为 true。-->
            <property name="rowBoundsWithCount" value="true"/>
        </plugin>
    </plugins>
3.接口:
    //通过PageHelper实现分页查询
   List<User> userPageHelper();
4.xml配置文件:
    <!--分页查询数据PageHelper-->
    <select id="userPageHelper" resultMap="user">
        select * from user
    </select>
5.测试:
    //PageHelper实现分页查询
    @Test
    public void userPageHelper() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //1.调用静态方法,开始分页
        PageHelper.startPage(-1,3);
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> users = mapper.userPageHelper();

        System.out.println(users);
        sqlSession.close();
    }

8.注解开发

重点:

有多个参数时候,所有的参数都要加上@Param注解,参数传递后以注解中的字段为主

自动提交事务,需要在mybatis配置文件中设置,但是不建议使用自动提交,以免提交错误的信息

  //设置自动提交事务,不需要手动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

手动提交:

 sqlSession.commit();
id自增的实现

注解:

添加数据(注解和xml配置):(id自增)

添加注解:

    //注解添加数据(id自增)
    @Insert({"insert into user(name,pwd) values(#{name},#{password})"})
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    Integer addUserAnoa(User user);

测试:

    //通过注解来提加用户
    @Test
    public void addUserAnoa(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setName("自增测试");
        user.setPassword("123456");
        logger.info("自增id的添加测试");
        Integer integer = mapper.addUserAnoa(user);
        sqlSession.close();
    }

非注解:

接口:

    //添加数据,自增id
    Integer addUserAutoId(User user);

xml配置:

    <!-- 添加数据,自增id -->
    <!--useGeneratedKeys默认值为false,keyProperty的值对应的是User类中的主键字段名,keyColumn对应的是数据库中的字段名-->
    <insert id="addUserAutoId" parameterType="cn.syp.entity.User" useGeneratedKeys="true" keyProperty="id"
            keyColumn="id">
    insert into user (name ,pwd) values (#{name},#{password});
    </insert>

测试:

    //添加用户信息,id自增,使用xml格式
    @Test
    public void addUserAutoId() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        logger.info("addUserAutoId====");
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setName("自增测试xml");
        user.setPassword("123456");
        Integer integer = mapper.addUserAutoId(user);
        if (integer > 0){
            System.out.println("添加成功");
        }else {
            System.out.println("添加失败");
        }

        sqlSession.close();
    }
8.1 面向接口编程
  • 接口是一种规范和约束,更高层的抽象更像是一类行为

  • 面向接口编程只是代码层体现的一种格式体现而已,真正的面向接口设计更贴近面向行为编程

除了映射器类,还有另一种方法来完成语句映射。 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。

8.2 注解开发
  1. 添加注解
   //注解查询全部数据
   @Select("select * from user")
   List<User> getUsersAllAnoat();
2. 核心配置文件中绑定接口
    <mappers>
        <!--注解的使用绑定-->
        <mapper class="cn.syp.dao.UserMapper"></mapper>
    </mappers>
  1. 测试:
    //通过注解来获取全部的用户
    @Test
    public void getUserAllAnoa(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> usersAllAnoat = mapper.getUsersAllAnoat();
        for (User user : usersAllAnoat) {
            System.out.println(user);
        }
        sqlSession.close();
    }

MyBatis自行流程:

8.3 注解实现CRUD

自动提交事务,需要在mybatis配置文件中设置:

  //设置自动提交事务,不需要手动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
@Param注解

有多个参数时候,所有的参数都要加上@Param注解,参数传递后以注解中的字段为主

注解查询:

添加注解:

  //有多个参数时候,所有的参数都要加上@Param注解,参数传递后以注解中的字段为主
    //注解查询数据
    @Select("select * from user where id = #{id}")
    List<User> getUserByIdAnoa(@Param("id") Integer id);

测试:

    //通过注解来获取用户:id
    @Test
    public void getUserByIdAnoa(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> userByIdAnoa = mapper.getUserByIdAnoa(1);
        for (User user : userByIdAnoa) {
            System.out.println(user);
        }
        sqlSession.close();
    }
注解添加:(id不自增)

添加注解:

    //注解添加数据
    @Insert("insert into user (id,name,pwd) values(#{id},#{name},#{password})")
    Integer addUserAnoan(User user);  

测试:

        //通过注解来提加用户
    @Test
    public void addUserAnoan(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Integer result = mapper.addUserAnoan(new User(10, "第十个不自增", "1234546"));
        logger.info("不自增id的添加测试");
        sqlSession.close();
    }
注解添加:(id自增)

添加注解:

    //注解添加数据(id自增)
    @Insert({"insert into user(name,pwd) values(#{name},#{password})"})
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    Integer addUserAnoa(User user);

测试:

    //通过注解来提加用户
    @Test
    public void addUserAnoa(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setName("自增测试");
        user.setPassword("123456");
        logger.info("自增id的添加测试");
        Integer integer = mapper.addUserAnoa(user);
        sqlSession.close();
    }
注解修改数据:

添加注解:

      //注解修改数据
    @Update("update user set name= #{name},pwd = #{password} where id = #{id}")
    Integer updateUserAnoa(User user);

测试:

    //通过注解来修改用户
    @Test
    public void updateUserAnoa(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Integer integer = mapper.updateUserAnoa(new User(10,"2354","3352"));
        if (integer > 0){
            System.out.println("修改成功");
        }else{
            System.out.println("修改失败");
        }
        sqlSession.close();
    }

注解删除数据:

添加注解:

    //注解删除数据
    @Delete("delete from user where id = #{id}")
    Integer deleteUserByIdAnoa(@Param("id") Integer id);

测试:

    //通过注解来删除用户
    @Test
    public void deleteUserByIdAnoa(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Integer integer = mapper.deleteUserByIdAnoa(10);
        if (integer > 0){
            System.out.println("删除成功");
        }else {
            System.out.println("删除失败");
        }
        sqlSession.close();
    }

9. 复杂查询

CREATE TABLE teacher(
	id INT(10) NOT NULL PRIMARY KEY,
	name VARCHAR(20) DEFAULT NULL
) ENGINE=INNODB DEFAULT CHARSET(UTF8);

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


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

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');
INSERT INTO student(id,name,tid) VALUES(6,'小杨','1');

搭建环境,测试通过.

1.多对一查询

根据学生的信息,以及对应的老师的信息

select * FROM student s ,teacher t WHERE s.tid = t.id;

删除冗余的数据查询:

select s.id,s.`name`,t.id,t.`name` FROM student s ,teacher t WHERE s.tid = t.id;

按照查询嵌套查询:

----- 子查询

接口:
    //根据学生的信息,以及对应的老师的信息
    public List<Student> getStudentAll();
    //根据学生的id来对应老师
    List<Teacher> getTeacherAllToS();
xml配置文件:
<!-- 结果映射-->
    <resultMap id="StudentToTeacher" type="cn.one.entity.Student">
        <!--column对应数据库中的字段,property对应实体类中的属性-->
        <result property="id" column="id"/>
        <!--  teacher对应老师的id,需要特殊处理
                    association 》 对象
                    collection 》 集合-->
<!--        teacher是一个对象,用 association-->
        <association  property="teacher" column="tid" javaType="teacher" select="getTeacherAllToS"/>
<!--        <collection property="teacher"/>-->
  </resultMap>

<!--    查询所有的学生信息
        根据查询出来的学生的id来选择学生
-->
<!--    resultType在核心配置文件中已经起了别名-->
    <select id="getStudentAll" resultMap="StudentToTeacher">
      select * from  student;
  </select>

    <select id="getTeacherAllToS" resultType="teacher">
      select * from  teacher where id =#{id};
  </select>
测试:
    //查询全部学生信息
    @Test
    public void getStudentAll(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentAll = mapper.getStudentAll();
        logger.info("查询全部学生===");
        for (Student student : studentAll) {
            System.out.println(student);
        }
        sqlSession.close();
    }
结果:

按照结果嵌套查询(推荐):

------ 嵌套查询

接口:

    //根据结果来嵌套查询
    public List<Student> getStudentAll2();

xml配置文件:

 <!--    根据结果来去嵌套查询-->
    <select id="getStudentAll2" resultMap="StudentToTeacher2">
        select s.id sid,s.name sname,t.id tid,t.name tname
        FROM student s ,teacher t
        WHERE s.tid = t.id;
    </select>
    <resultMap id="StudentToTeacher2" type="student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--teacher是复杂查询,需要特殊处理-->
        <association property="teacher" javaType="teacher">
            <!--  嵌套查询,,在去对应老师的字段中的 数据-->
            <result property="name" column="tname"/>
        </association>
    </resultMap>

测试:

    //根据结果嵌套查询
    //查询全部学生信息
    @Test
    public void getStudentAll2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentAll2 = mapper.getStudentAll2();
        logger.info("查询全部学生:根据结果嵌套查询===");
        for (Student student : studentAll2) {
            System.out.println(student);
        }
        sqlSession.close();
    }

结果:

2.一对多查询

环境搭建,测试通过

根据老师的信息,即指定老师,将老师对应下的所有的学生给映射出来

SELECT * 
FROM student s,teacher t
where s.tid = t.id ;

精简之后:

SELECT t.id, t.name,s.id,s.name 
FROM student s,teacher t
where s.tid = t.id ;

按照查询嵌套查询:

— 子查询

接口:

    //根据老师的信息,即指定老师,将老师对应下的所有的学生给映射出来
    //按照查询嵌套查询
    Teacher1 getTeacher2(@Param("tid") Integer id);

xml配置文件:

    <!--  根据查询嵌套查询-->
    <select id="getTeacher2" resultMap="getTeacher2">
        select * from teacher t where id= #{tid}
    </select>
    <!--    子查询-->
    <resultMap id="getTeacher2" type="teacher">
        <result column="id" property="id"/>
        <result column="name" property="name"/>
<!--        返回老师对象,将老师对象的id传给调用的子查询 column="id"-->
        <collection property="student1s" javaType="ArrayList" ofType="student" select="selectStudentById" column="id"/>

    </resultMap>
    <!--    通过老师的id来查询学生信息 -->
    <select id="selectStudentById" resultType="teacher">
            select * from student where  tid=#{id}
    </select>

测试:

    //根据老师的信息,即指定老师,将老师对应下的所有的学生给映射出来
    //按照查询嵌套查询
    @Test
    public void getTeacher2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher1 teacher2 = mapper.getTeacher2(1);
        System.out.println(teacher2);
        sqlSession.close();
    }

结果:

按照结果嵌套查询(推荐):

------ 嵌套查询

接口:

    //根据老师的信息,即指定老师,将老师对应下的所有的学生给映射出来
    Teacher1 getTeacher(@Param("tid") Integer id);

xml配置文件:

<!--    按照结果嵌套查询 根据老师的信息,即指定老师,将老师对应下的所有的学生给映射出来-->
    <select id="getTeacher"  resultMap="getStudentByTeacher" >
        SELECT t.id tid, t.name tname,s.id sid, s.name sname
        FROM student s,teacher t
        where s.tid = t.id and t.id=#{tid};
    </select>
    
    <resultMap id="getStudentByTeacher" type="teacher">
        <result property="id" column="tid"></result>
        <result property="name" column="tname"></result>
        <!--  teacher对应老师的id,需要特殊处理
            association 》 对象
            collection 》 集合

            javaType : 指定属性的类型

            集合中的泛型信息,我们使用ofType获取
            -->
        <!--        teacher是一个对象,用 association 泛型信息 使用ofType获取-->
        <collection property="student1s" ofType="student">
            <result property="id" column="sid"></result>
            <result property="name" column="sname"></result>
            <result property="tid" column="tid"></result>
        </collection>
    </resultMap>

测试:

    //根据老师的信息,即指定老师,将老师对应下的所有的学生给映射出来
    @Test
    public void getStudentByTeacher(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher1 teacher = mapper.getTeacher(1);
        System.out.println(teacher);

        sqlSession.close();
    }

结果:

小结:

javaType

​ 用来指定实体类中属性的类型

ofType

​ 用来指定映射到List或者集合中的实体类类型,泛型中的约束类型

10 . 动态 SQL

动态SQL

系统能够根据需要的条件生成不同的SQL语句

  • 动态 SQL 是 MyBatis 的强大特性之一。
  • 根据不同条件拼接 SQL 语句很痛苦吗,利用动态 SQL,可以彻底摆脱这种痛苦。

元素种类

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

SQL准备:

CREATE TABLE blog(
	id INT(20) NOT NULL COMMENT '博客id',
	title VARCHAR(20) NOT NULL COMMENT '标题',
	author VARCHAR(20) NOT NULL COMMENT '作者',
	create_time datetime NOT NULL COMMENT '创建时间',
	views INT(30) NOT NULL COMMENT '浏览量'
) ENGINE=INNODB DEFAULT CHARSET=utf8;

实体类的字段名和数据库的字段名不一致:

工具类:

随机生成id工具类:

import org.junit.Test;

import java.util.UUID;

/**
 * @description: 随机生成id
 * @return
 */

public class IDUtils {

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

    //测试生成的id
    @Test
    public void test(){
        System.out.println(IDUtils.getId());
    }
}

配置:

<!-- 驼峰命名法-->
<setting name="mapUnderscoreToCamelCase" value="true"/>

实体类:

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

import java.util.Date;

/**
 * @description:blog实体类
 * @return
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {

    private  String id;
    private String title;
    private String author;
    private Date createTime; //实体属性名和数据库字段名不一致,使用mybatis的大小写的驼峰命名
    private Integer views;
}

dao层的Mapper:

    //插入数据
    Integer addBlog(Blog blog);

dao层的Mapper.xml

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

测试

  //测试添加博客信息
    @Test
    public  void addBlogTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog = new Blog();
        blog.setId(IDUtils.getId());
        blog.setTitle("博客2");
        blog.setAuthor("s355");
        blog.setCreateTime(new Date());
        blog.setViews(300);

        mapper.addBlog(blog);

        blog.setTitle("博客搭嘎");
        blog.setAuthor("sgyoas");
        blog.setViews(4500);
        mapper.addBlog(blog);

        blog.setTitle("嘎嘎嘎嘎嘎");
        blog.setAuthor("我挑花眼");
        blog.setViews(4670);
        mapper.addBlog(blog);

        blog.setTitle("的实干为何");
        blog.setAuthor("s是放假啦");
        blog.setViews(450);
        mapper.addBlog(blog);

        sqlSession.commit();
        sqlSession.close();
    }

if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

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

这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。

如果希望通过 “title” 和 “author” 两个参数进行可选搜索该怎么办呢?首先,我想先将语句名称修改成更名副其实的名称;接下来,只需要加入另一个条件即可。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <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>

mapper接口:

    //赶紧传入的作者,如果有作者,就把作者的所有博客查出来,如果没有该作者,就查询全部数据
    List<Blog> selectByAuthorAndTitle(Map map);

mapper.xml文件:

<!--赶紧传入的作者,如果有作者,就把作者的所有博客查出来,如果没有该作者,就查询全部数据-->
<select id="selectByAuthorAndTitle" 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 selectByAuthorAndTitle(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        map.put("title", "博客");
        map.put("author", "sgyoas");
        List<Blog> blogs = mapper.selectByAuthorAndTitle(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }

choose (when, otherwise)

— 有点像 Java 中的 switch 语句

接口:

    //通过choose选择全部的博客
    List<Blog> selectChoosse(Map map);

Mapper.xml配置:

传入了 “title” 就按 “title” 查找,

传入了 “author” 就按 “author” 查找的情形。

两者都传入了,就按顺序优先查找。

若两者都没有传入,就返回otherwise中的查询

   <!-- 通过choose选择全部的博客-->
    <select id="selectChoosse" parameterType="map" resultType="Blog">
        select  * from blog
        <where>
            <choose>
                <when test="title != null">
                    title like #{title}
                </when>
                <when test="author != null">
                   and author = #{author}
                </when>
               <otherwise>
                   and views = 450
               </otherwise>
            </choose>
        </where>
    </select>

测试:

    //通过choose选择全部的博客
    @Test
    public void selectChoose(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
//        map.put("title", "%客%");
//        map.put("author", "sgyoas");
//        map.put("views", "4670");

        List<Blog> blogs = mapper.selectChoosse(map);

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

trim (where)

接口:

    //通过trim、where、set 选择全部的博客
    List<Blog> selectChoose(Map map);

Mapper.xml配置:

        <!-- 通过trim、where、set 选择全部的博客客-->
<!--where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。-->
    <select id="selectChoose" parameterType="map" resultType="Blog">
        select  * from blog
        <where>
            <if test="author != null">
                author = #{author}
            </if>
            <if test="title != null">
                and title like #{title}
            </if>
        </where>
    </select>

测试:

    //通过trim、where、set 选择全部的博客
    @Test
    public void selectChoose(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        map.put("title", "%客%");
        map.put("author", "sgyoas");

        List<Blog> blogs = mapper.selectChoose(map);

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

结果:

trim ( set)

set 元素可以用于动态包含需要更新的列,忽略其它不更新的列

set 元素可以用于动态包含需要更新的列,忽略其它不更新的列

接口:

    //通过set更新博客
    Integer updateSet(Map map);

Mapper.xml配置:

    <!-- 通过set更新博客-->
    <update id="updateSet" parameterType="map">
        update blog
        <set>
            <if test="title != null">title = #{title},</if>
            <if test="author != null">author = #{author}</if>
        </set>
        where id = #{id}
    </update>

测试:

  //通过set更新博客
    @Test
    public void updateSet(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        map.put("title", "博客23424");
        map.put("author", "sypsa");
        map.put("id", "762dca1074de4dc3af206665331f9ad3");

        Integer integer = mapper.updateSet(map);
        System.out.println(integer);

        sqlSession.commit();
        sqlSession.close();
    }

结果:

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

SQL片段 – sql

— 将查询语句中的一些重复度大,公共使用的片段,来提取出来,进行复用

— 通过sql标签来进行sql片段的定义,然后在需要使用的地方通过include的refid引用属性进行引用

定义SQL片段:

    <!-- SQL片段-->
    <!-- 在需要使用的地方通过include的refid引用属性进行引用-->
    <sql id="selectTrime-sql">
        <if test="author != null">
            author = #{author}
        </if>
        <if test="title != null">
            and title like #{title}
        </if>
    </sql>

引用:

    <select id="selectTrime" parameterType="map" resultType="Blog">
        select  * from blog
        <where>
        <include refid="selectTrime-sql"></include>
        </where>
    </select>

SQL片段注意事项:

  • 查询难度大的不要定义SQL片段,会影响效率
  • SQL片段中不要出现where语句

foreach 实现

select * from blog where 1=1 and (id=1 or id=2 or id=3);
select * 
from blog 
where 1=1 and 
  <foreach item="id" collection="idList"
      open="(" separator="or" close=")">
        #{id}
  </foreach>
(id=1 or id=2 or id=3);

准备:

select * from `user` where 1=1 and (id=1 or id=2 or id=3 or id= 4);
接口:
    //使用foreach查询用户数据
    List<User> getUserByEach(Map map);
Mapper.xml配置:
<!--  使用foreach查询用户数据-->
<!-- select * from `user` where 1=1 and (id=1 or id=2 or id=3 or id= 4);;-->
<!--  传递一个万能的map,map中存在一个集合-->
    <select id="getUserByEach" resultType="forUser" parameterType="map">
        select * from user
        <where>
        <foreach collection="idList" item="id" open="and (" separator="or" close=")">
        id = #{id}
        </foreach>
        </where>
    </select>
测试:
 //使用foreach查询用户数据   
	@Test
    public void getUserByEach(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap map = new HashMap();

        ArrayList<Integer> idList = new ArrayList<Integer>();

        idList.add(1);
        idList.add(2);
        idList.add(3);
        idList.add(4);

        map.put("idList", idList);

        List<User> userByEach = mapper.getUserByEach(map);

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

        sqlSession.close();
    }
结果:

动态SQL就是根据自己的需要去动态的拼接SQL,只要拼接出来的SQL语句是正确的,那就是可以正确执行

  • 需要先在sql中自己试验自己的sql是否能正确查询
  • 查询正确后,在去进行拼接

11. 缓存

– 什么是缓存

​ 缓存就是数据交换的缓冲区

– 缓存能解决的问题

  • 减少和数据库的交互

  • 提升性能

  • 缓解数据库压力:通过缓存能够大大降低数据库的压力。

– 缓存有以下三种模式:

  • Cache Aside 更新模式
  • Read/Write Through 更新模式
  • Write Behind Caching 更新模式

– 适用场景

  • 对于数据实时性要求不高

    对于一些经常访问但是很少改变的数据,读明显多于写,适用缓存就很有必要。

  • 对于性能要求高: 比如一些秒杀活动场景。

– 不适用场景

  • 经常查询并且经常修改的数据

Mybatis缓存

import org.apache.ibatis.cache.Cache; //Cache接口

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。

Mybatis缓存有两级:

  • 一级缓存:默认开启,执行sql时,就会打开一级缓存,sql执行完成后,就会释放
  • 二级缓存 : 基于nameSpace级别的缓存,一个名称空间,对应一个二级缓存

默认只有一级缓存开启(也成为本地缓存)

二级缓存需要手动开启

也可以自定义缓存

一级缓存

测试步骤:

开启日志:

    <!--配置完整的 settings-->
    <settings>
        <!--日志工厂的实现-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

进行测试

查询的缓存

接口:

    //根据id查询用户数据
    List<User> getUserByIdOne(Integer id);

mapper.xml配置:

    <!--    根据id查询用户数据: getUserByIdOne-->
    <select id="getUserByIdOne" parameterType="cacheUser" resultMap="cacheUser">
        select  * from user where  id = #{id}
    </select>

测试:

    //缓存信息测试
    @Test
    public void cacheTest1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userByIdOne = mapper.getUserByIdOne(1);
        System.out.println(userByIdOne);
        System.out.println("============");
        List<User> userByIdOne1 = mapper.getUserByIdOne(1);
        System.out.println(userByIdOne1);

        sqlSession.close();

    }

结果:

增删改的缓存

接口:

    //根据id修改用户信息
    Integer updateUserById(Map<String,Object> map);

mapper.xml配置文件:

    <!-- 根据id修改用户信息-->
    <update id="updateUserById" parameterType="cacheUser" >
        update user  set name = #{name} where id = #{id}
    </update>

测试:

    //缓存信息测试
    @Test
    public void cacheTest1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userByIdOne = mapper.getUserByIdOne(1);
        System.out.println(userByIdOne);
        System.out.println("============");

        //修改操作
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("id", 11);
        map.put("name", "测试缓存");
        mapper.updateUserById(map);

        System.out.println("=+++++=======+++++=");
        List<User> userByIdOne1 = mapper.getUserByIdOne(1);
        System.out.println(userByIdOne1);

        sqlSession.close();
    }

结果:

缓存失效的情况

  • 查询的语句不一致时

  • 增删改会修改数据,需要重新读取

  • 对应的查询对象不一致时,会执行新的查询语句

  • 被执行者进行手动的缓存清理

    SqlSession sqlSession = MybatisUtils.getSqlSession();
    sqlSession.clearCache();//手动清理缓存
    

二级缓存

要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

基本上就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

这些属性可以通过 cache 元素的属性来修改。比如:

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

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

工作机制

  • 一个会话查询一条数据,完成之后数据就会在一级缓存中
  • 查询完成,关闭会话后,一级缓存就会失效,此时就只能将缓存保存到二级缓存中,延长时间
  • 新的会话开启查询信息后,就能从二级缓存中直接获取内容
  • 不同的mapper查询出的数据会放在mapper对应的个子的缓存中

步骤

1.在核心配置文件中开启全局缓存:

   <settings>
        <!-- 显示的全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。-->
        <setting name="cacheEnabled" value="true"/>
</settings>

2.在需要使用二级缓存的mapper.xml中开启二级缓存

 <!--在当前mapper.xml中使用二级缓存-->
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="4"
  readOnly="true"/>

测试:

    //二级缓存信息测试
    @Test
    public void cacheTest2(){
        //开启两个sqlsession
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        SqlSession sqlSession2 = MybatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);

        List<User> userByIdOne = mapper.getUserByIdOne(1);
        System.out.println(userByIdOne);

        sqlSession.close();
        
        System.out.println("=+++++=======+++++=");
        List<User> userByIdOne1 = mapper2.getUserByIdOne(1);
        System.out.println(userByIdOne1);

        sqlSession2.close();
    }

结果:

自定义缓存-- ehcache

ehcache

  • EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点。

  • Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

  • Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Ehcache),但本身不直接提供缓存功能的实现。它支持注解方式使用缓存,非常方便。

特性:

  1. 快速
  2. 简单
  3. 多种缓存策略
  4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题
  5. 缓存数据会在虚拟机重启的过程中写入磁盘
  6. 可以通过RMI、可插入API等方式进行分布式缓存
  7. 具有缓存和缓存管理器的侦听接口
  8. 支持多缓存管理器实例,以及一个实例的多个缓存区域
  9. 提供Hibernate的缓存实现

步骤

  1. 导包
        <dependency>
            <!--  ehcache依赖包-->
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>
  1. 在mapper.xml中使用

    <!--    自定义的缓存应用-->
        <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    

可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。

<cache type="com.domain.something.MyCustomCache"/>

最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。

  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

[外链图片转存中…(img-inXSEA6J-1606466381158)]

这些属性可以通过 cache 元素的属性来修改。比如:

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

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

工作机制

  • 一个会话查询一条数据,完成之后数据就会在一级缓存中
  • 查询完成,关闭会话后,一级缓存就会失效,此时就只能将缓存保存到二级缓存中,延长时间
  • 新的会话开启查询信息后,就能从二级缓存中直接获取内容
  • 不同的mapper查询出的数据会放在mapper对应的个子的缓存中

步骤

[外链图片转存中…(img-EXxnd9fj-1606466381160)]

1.在核心配置文件中开启全局缓存:

   <settings>
        <!-- 显示的全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。-->
        <setting name="cacheEnabled" value="true"/>
</settings>

2.在需要使用二级缓存的mapper.xml中开启二级缓存

 <!--在当前mapper.xml中使用二级缓存-->
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="4"
  readOnly="true"/>

测试:

    //二级缓存信息测试
    @Test
    public void cacheTest2(){
        //开启两个sqlsession
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        SqlSession sqlSession2 = MybatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);

        List<User> userByIdOne = mapper.getUserByIdOne(1);
        System.out.println(userByIdOne);

        sqlSession.close();
        
        System.out.println("=+++++=======+++++=");
        List<User> userByIdOne1 = mapper2.getUserByIdOne(1);
        System.out.println(userByIdOne1);

        sqlSession2.close();
    }

结果:

[外链图片转存中…(img-sO5wsfc6-1606466381168)]

自定义缓存-- ehcache

ehcache

  • EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点。

  • Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

  • Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Ehcache),但本身不直接提供缓存功能的实现。它支持注解方式使用缓存,非常方便。

特性:

  1. 快速
  2. 简单
  3. 多种缓存策略
  4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题
  5. 缓存数据会在虚拟机重启的过程中写入磁盘
  6. 可以通过RMI、可插入API等方式进行分布式缓存
  7. 具有缓存和缓存管理器的侦听接口
  8. 支持多缓存管理器实例,以及一个实例的多个缓存区域
  9. 提供Hibernate的缓存实现

步骤

  1. 导包
        <dependency>
            <!--  ehcache依赖包-->
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>
  1. 在mapper.xml中使用

    <!--    自定义的缓存应用-->
        <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    

可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。

<cache type="com.domain.something.MyCustomCache"/>

使用一个自定义的缓存实现。type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器。 这个接口是 MyBatis 框架中许多复杂的接口之一,但是行为却非常简单。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值