Day18-MyBatis

MyBatis(持久层框架)

  1. 如何获得Mybatis?

持久化

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

  • 内存:断电及失

  • 数据库(jdbc),io文件持久化

为什么需要持久化?

  • 有一些对象不能让他丢掉

  • 内存太贵

持久层

Dao层,service层,Controller层

  • 完成持久化工作的代码块

  • 层界限十分明显

为什么需要Mybatis?

  • 方便

  • 传统的jdbc代码太复杂了,简化,框架,自动化

  • 帮助程序员将数据存入到数据库中

  • 优点:

    • 简单易学

    • 灵活

    • sql和代码的分离,提高了可维护性

    • 提供映射标签,支持对象与数据库的orm字段关系映射

    • 提供对象关系映射标签,支持对象关系组建维护

    • 提供xml标签,支持编写动态sql

第一个Mybatis程序

1.1 搭建环境

搭建数据库

CREATE DATABASE `mybatis`;

USE `mybatis`;

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'),
(4,'小玉','123456')

新建项目

  1. 新建一个普通的maven项目

  2. 删除src目录

  3. 导入maven依赖

1.2创建一个模块

  • 编写mybatis的核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
       PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
       "https://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.jdbc.Driver"/>
               <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
               <property name="username" value="root"/>
               <property name="password" value="12345678"/>
           </dataSource>
       </environment>
   </environments>

</configuration>
  • 编写mybatis工具类

package com.lsq.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 javax.swing.plaf.synth.SynthToolTipUI;
import java.io.IOException;
import java.io.InputStream;

/**
* @author liushaoqin
* @version 1.0
* @sqlSessionFactory sqlSession
*/
public class MybatisUtils {
   private static SqlSessionFactory sqlSessionFactory;
   static{
       try {
           //使用Mybatis第一步:获取sqlSessionFactory对象
           String resource="mybatis-config.xml";
           InputStream inputStream = null;
           inputStream = Resources.getResourceAsStream(resource);
           sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      } catch (IOException e) {
           throw new RuntimeException(e);
      }
  }

   //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
   public static SqlSession getSqlSession(){
       return sqlSessionFactory.openSession();
  }
}

1.3 编写代码

  • 实体类

    package com.lsq.pojo;

    /**
    * @author liushaoqin
    * @version 1.0
    */
    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.lsq.dao;

    import com.lsq.pojo.User;

    import java.util.List;

    /**
    * @author liushaoqin
    * @version 1.0
    */
    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"
           "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--namespace绑定一个Dao/Mapper接口-->
    <mapper namespace="com.lsq.dao.UserDao">
       <!--select查询语句-->
       <select id="getUserList" resultType="com.lsq.pojo.User">
           select * from myBatis.user  where id = #{id}
       </select>
    </mapper>

1.4 测试

注意点:

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

核心配置文件中注册mappers

  • junit

    package com.lsq.dao;

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

    import java.util.List;

    /**
    * @author liushaoqin
    * @version 1.0
    */
    public class UserDaoTest{
       @Test
       public void test(){
           //第一步:获得sqlsession对象
           SqlSession sqlSession = MybatisUtils.getSqlSession();

           //方式一:getMapper
           UserDao userDao = sqlSession.getMapper(UserDao.class);
           List<User> userList = userDao.getUserList();
           for (User user : userList) {
               System.out.println(user);
          }

           //关闭sqlSession
           sqlSession.close();
      }
    }

    可能会遇到的问题:

    1. 配置文件没有注册

    2. 绑定接口错误

    3. 方法名不对

    4. 返回类型不对

    5. 查询语句写错

    6. maven导出资源问题

    7. 数据库驱动com.mysql.cj.jdbc.Driver

CRUD

1.namespace:命名空间
  • namespace中的包名要和Dao/mapper接口中的包名一致

2.Select
  • id:就是对应的namespace中的方法名;

  • resultType:Sql语句执行的返回值

  • parameterType:参数类型

  1. 编写接口

    User getUserById(int id);
  2. 编写对应的mapper中的sql语句

    <!--select查询语句-->
       <select id="getUserById" parameterType="int" resultType="com.lsq.pojo.User">
          select * from myBatis.user where id=#{id}
       </select>
  3. 测试

     @Test
       public void getUserById(){
           SqlSession sqlSession = MybatisUtils.getSqlSession();
           UserMapper usermapper = sqlSession.getMapper(UserMapper.class);

           User user = usermapper.getUserById(1);
           System.out.println(user);

           sqlSession.close();
      }
3.Insert
  <!--insert插入语句,对象中的属性可以直接取出来,和数据库中的名称要一致-->
   <insert id="addUser" parameterType="com.lsq.pojo.User">
      insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd})
   </insert>
4.Update
 <!--update修改语句-->
   <update id="updateUser" parameterType="com.lsq.pojo.User">
      update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
   </update>
5.Delete
 <!--delete删除语句-->
   <delete id="deleteUser" parameterType="int">
      delete from mybatis.user where id=#{id}
   </delete>

注意点:

  • 增删查改需要提交事务

Map

假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map

//map
int addUser2(Map<String, Object> map);
 <!--insert插入语句,对象中的属性可以直接取出来,传递map中的key-->
   <insert id="addUser2" parameterType="map">
      insert into mybatis.user(id,name,pwd) values(#{userid},#{username},#{password})
   </insert>
 @Test
   public void addUser2(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();
       UserMapper usermapper = sqlSession.getMapper(UserMapper.class);

       Map<String, Object> map = new HashMap<>();
       map.put("userid",6);
       map.put("username","张张");
       map.put("password","123456");
       int result = usermapper.addUser2(map);
       if (result>0) {
           System.out.println("插入成功");
      }
       //提交事务
       sqlSession.commit();
       sqlSession.close();
  }

Map传递参数,直接在sql中取出key即可 【parameterType="map"】

对象传递参数,直接在sql中取对象的属性即可 【parameterType="Object"】

只有一个参数的情况下,可以直接在sql中取到 【parameterType="int"】

多个参数用Map,或者注解

思考题

模糊查询怎么写?

  1. Java代码执行的时候,传递通配符% %

    List<User> userList = usermapper.getUserLike("文");
  1. 在sql拼接中使用通配符

     <!--模糊查询-->
    <select id="getUserLike" parameterType="String" resultType="com.lsq.pojo.User">
           select * from myBatis.user where name like "%"#{value}"%"
    </select>

配置解析

1. 核心配置文件
  • Mybatis-config.xml

  • MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性2

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

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

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

3. 属性(properties)

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

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

编写一个配置文件

db.properties

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

在核心配置文件中引入

 <!--引入外部配置文件,当内部和外部配置文件有相同的的参数配置时,优先使用外部配置文件-->
  <properties resource="db.properties">
      <property name="username" value="root"/>
      <property name="pwd" value="11111"/>
  </properties>
  • 可以直接引入外部文件

  • 可以在其中增加一些属性配置

  • 如果两个文件有同一个配置,优先使用外部配置文件

4. 类型别名(typeAliases)
  • 类型别名可为 Java 类型设置一个缩写名字

  • 意在降低冗余的全限定类名书写

 <!--可以给实体类起别名-->
   <typeAliases>
       <typeAlias type="com.lsq.pojo.User" alias="User"/>
   </typeAliases>
  • 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean

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

<!--可以给实体类起别名-->
   <typeAliases>
       <package name="com.lsq.pojo"/>
   </typeAliases>

在实体类比较少的时候,使用第一种方式

如果在实体类比较多的时候,建议使用第二种

第一种可以自定义别名,第二种则“不行”,如果非要改,需要在实体类上增加注解

@Alias("author")
public class Author {
  ...
}
5. 配置
  • 这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为

6. 映射器(mappers)

MapperRegistry:注册绑定我们的Mapper文件

方式一:

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

方式二:使用class文件绑定注册

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

注意点:

  • 接口和他的Mapper配置文件必须同名

  • 接口和他的Mapper配置文件必须在同一个包下

方式三:使用扫描包进行绑定注入

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

注意点:

  • 接口和他的Mapper配置文件必须同名

  • 接口和他的Mapper配置文件必须在同一个包下

7. 生命周期和作用域

SqlSessionFactoryBuilder

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

  • 局部变量

SqlSessionFactory

  • 可以想象为数据库连接池

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

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

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

SqlSession

  • 每个线程都应该有它自己的 SqlSession 实例

  • 连接到连接池的一个请求

  • 用完之后需要赶紧关闭,否则资源被占用

  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域

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

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

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

}

测试出现问题

select * from myBatis.user where id=#{id}
//类型处理器
select id,name,pwd from myBatis.user where id=#{id}

解决方法:

  • 起别名

    <!--select查询语句-->
       <select id="getUserById" parameterType="int" resultType="com.lsq.pojo.User">
          select id,name,pwd as password from myBatis.user where id=#{id}
       </select>
  1. resultMap

    结果集映射

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

       <!--select查询语句-->
       <select id="getUserById" parameterType="int" resultMap="UserMap">
           <!--select * from myBatis.user where id=#{id}-->
          select id,name,pwd as password from myBatis.user where id=#{id}
       </select>
  • resultMap 元素是 MyBatis 中最重要最强大的元素

  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了

日志

1. 日志工厂

如果一个数据库操作,出现了异常,我们需要排错

曾经:sout、debug

现在:日志工厂

  • SLF4J

  • Apache Commons Logging

  • Log4j 2

  • Log4j (3.5.9 起废弃)【掌握】

  • JDK logging

  • COMMONS_LOGGING

  • STDOUT_LOGGING 【掌握】

  • NO_LOGGING

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

STDOUT_LOGGING标准日志输出

在mybatis的核心日志文件中,配置我们的日志

Log4j

  1. 先导入log4j的包

 <dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
</dependency>
  1. log4j.properties

    #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
    log4j.rootLogger=debug,console,file

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

    #文件输出的相关设置
    log4j.appender.file=org.apache.log4j.FileAppender
    log4j.appender.file.File=/Users/liushaoqin/Desktop/java学习/log/lsq.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.PrepareStatement=DEBUG
  2. 配置log4j为日志的实现

      <settings>
        <setting name="logImpl" value="LOG4J"/>
     </settings>
  3. Log4j的使用

说明

  1. 在要使用Log4j的类中,导入包import org.apache.log4j.Logger;

  2. 日志对象,参数为当前类的class

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

    logger.info("info:进入了testLog4j");
    logger.debug("debug:进入了testLog4j");
    logger.error("error:进入了testLog4j");

分页

使用Limit分页

语法:select * from mybatis.user limit startIndex,pageSize;
    select * from mybatis.user limit 3; #[0,3)

使用Mybatis实现分页,核心SQL

  1. 接口

     //分页
       List<User> getUserByLimit(Map<String,Integer> map);
  2. Mapper.XML

     <!--分页查询-->
       <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
          select * from myBatis.user limit #{startIndex},#{pageSize}
       </select>
  3. 测试

     @Test
       public void getUserByLimit(){
           SqlSession sqlSession = MybatisUtils.getSqlSession();
           UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

           HashMap<String, Integer> map = new HashMap<>();
           map.put("startIndex",1);
           map.put("pageSize",2);

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

RowBounds分页

不再使用SQL实现分页

  1. 接口

    //分页2
    List<User> getUserByRowBounds();
  2. mapper.xml

     <!--分页查询2-->
       <select id="getUserByRowBounds" resultMap="UserMap">
          select * from myBatis.user
       </select>
  3. 测试

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

           //通过java代码层面实现分页
           List<User> userList = sqlSession.selectList("com.lsq.dao.UserMapper.getUserByRowBounds",null,rowBounds);
           for (User user : userList) {
               System.out.println(user);
          }

           sqlSession.close();
      }

    分页插件

    mybatis pagehelper

    使用注解开发

    1. 注解在接口上实现

       @Select("select * from user")
         List<User> getUsers();
    2. 需要在核心配置文件中绑定接口

        <!--绑定接口-->
         <mappers>
             <mapper class="com.lsq.dao.UserMapper"/>
         </mappers>
    3. 测试

      @Test
         public void test() {

             SqlSession sqlSession = MybatisUtils.getSqlSession();

             //底层主要通过反射
             UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
             List<User> users = userMapper.getUsers();
             for (User user : users) {
                 System.out.println(user);
            }

             sqlSession.close();
        }

      本质:反射机制实现

      底层:动态代理

    CRUD

    我们可以在工具类创建的时候实现自动提交事务

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

    编写接口,增加注解

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

       //引用对象不需要使用@Param("")注解
       @Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
       int addUser(User user);

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

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

    测试类

    package dao;

    import com.lsq.dao.UserMapper;
    import com.lsq.pojo.User;
    import com.lsq.utils.MybatisUtils;
    import org.apache.ibatis.session.RowBounds;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.log4j.Logger;
    import org.junit.Test;

    import java.util.HashMap;
    import java.util.List;

    /**
    * @author liushaoqin
    * @version 1.0
    */
    public class UserMapperTest {

       @Test
       public void test() {

           SqlSession sqlSession = MybatisUtils.getSqlSession();

           //底层主要通过反射
           UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //       List<User> users = userMapper.getUsers();
    //       for (User user : users) {
    //           System.out.println(user);
    //       }

           User userByID = userMapper.getUserByID(1);
           System.out.println(userByID);

           userMapper.addUser(new User(7, "大勋", "123456"));

           userMapper.updateUser(new User(7,"小妖艳","1234567"));

           userMapper.deleteUser(5);
      }

    }

    【注意:我们必须要讲接口注册绑定到我们的核心配置文件中】

    关于@Param()注解
    • 基本类型的参数或者String类型,需要加上

    • 引用类型不需要加

    • 如果只有一个基本类型的话,可以忽略,但是建议大家都加上

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

    Lombok

    1. 在IDEA中安装Lombok插件【新版idea已内置,不需要安装了】

    2. 在项目中导入lombok的jar包

    说明:

    @Data:无参构造,get,set,tostring,hashcode,equals
    @AllArgsConstructor
    @NoArgsConstructor
    @EqualsAndHashCode
    @ToString
    @Getter
    @Setter

    多对一处理

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

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

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

    INSERT INTO `student` (id,name,tid) VALUES('1','小明','1');
    INSERT INTO `student` (id,name,tid) VALUES('2','小红','1');
    INSERT INTO `student` (id,name,tid) VALUES('3','小张','1');
    INSERT INTO `student` (id,name,tid) VALUES('4','小李','1');
    INSERT INTO `student` (id,name,tid) VALUES('5','小王','1');
    测试环境搭建
    1. 导入lombok

    2. 新建实体类Teacher,Student

    3. 建立Mapper接口

    4. 建立Mapper.xml

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

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

    按照查询嵌套处理
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
           PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
           "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.lsq.dao.StudentMapper">

       <resultMap id="StudentTeacher" type="Student">
           <result property="id" column="id"/>
           <result property="name" column="name"/>
           <!--复杂的属性我们需要单独处理
           对象:association
           集合:collection
           -->
           <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
       </resultMap>
       
       <!--思路:
       1. 查询所有的学生信息
       2. 根据查询出来的学生的tid,寻找对应的老师
       -->

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

       <select id="getTeacher" resultType="Teacher">
          select * from teacher where id=#{id};
       </select>
    </mapper>
    按照结果嵌套处理
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
           PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
           "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.lsq.dao.StudentMapper">
       <resultMap id="StudentTeacher2" type="Student">
           <result property="id" column="sid"/>
           <result property="name" column="sname"/>
          <association property="teacher" javaType="Teacher">
              <result property="name" column="tname"/>
              <result property="id" column="tid"/>
          </association>
       </resultMap>

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

    一对多处理

    1. 环境搭建

      package com.lsq.pojo;

      import lombok.Data;

      import java.util.List;

      /**
      * @author liushaoqin
      * @version 1.0
      */
      @Data
      public class Teacher {
         private int id;
         private String name;

         //一个老师对应多个学生
         private List<Student> students;
      }
      package com.lsq.pojo;

      import lombok.Data;

      /**
      * @author liushaoqin
      * @version 1.0
      */
      @Data
      public class Student {
         private int id;
         private String name;
         private int tid;
      }
      按照结果嵌套处理
       <!--按结果嵌套查询-->
         <select id="getTeacher" resultMap="TeacherStudent">
            select s.id sid,s.name sname,s.tid,t.name tname,t.id tid
            from student s,teacher t
            where s.tid=t.id and t.id=#{id}
         </select>
         <resultMap id="TeacherStudent" type="Teacher">
             <result property="id" column="tid"/>
             <result property="name" column="tname"/>
             <!--
             javaType="" 指定属性的类型
             集合中的泛型信息,我们使用ofType获取
             -->
             <collection property="students" ofType="Student">
                 <result property="id" column="sid"/>
                 <result property="name" column="sname"/>
                 <result property="tid" column="tid"/>
             </collection>
         </resultMap>
      按照查询嵌套处理
      <!--按照查询嵌套处理-->
         <select id="getTeacher2" resultMap="TeacherStudent2">
            select * from teacher where id=#{id}
         </select>

         <resultMap id="TeacherStudent2" type="Teacher">
             <result property="id" column="id"/>
             <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId">
                 <result property="id" column="id"/>
             </collection>
         </resultMap>

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

    小结

    • 关联-association 【多对一】

    • 集合-collection 【一对多】

    • javaType & ofType

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

      • ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型


注意

  • 保证SQL的可读性,尽量保证通俗易懂

  • 注意一对多和多对一中,属性名和字段的问题

  • 如果问题不好排查错误,可以使用日志,建议使用Log4j

动态SQL

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

  1. 搭建环境

    CREATE TABLE blog
    (
    `id` varchar (50) NOT NULL COMMENT '博客id',
    `title` varchar (100) NOT NULL COMMENT '博客标题',
    `author` varchar(30) NOT NULL COMMENT '博客作者',
    create_time datetime NOT NULL COMMENT '创建时间',
    `views` int (30) NOT NULL COMMENT '浏览量'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  2. 创建一个基础工程

    • 导包

    • 编写配置文件

    • 编写实体类

      package com.lsq.pojo;

      import lombok.Data;

      import java.util.Date;

      /**
      * @author liushaoqin
      * @version 1.0
      */
      @Data
      public class Blog {
         private String id;
         private String tittle;
         private String author;
         private Date createTime;
         private int views;
      }
    • 编写实体类对应Mapper接口和Mapper.xml文件

IF

  • 编写Mapper接口

    //查询博客
       List<Blog> queryBlogIF(Map map);
  • 编写Mapper.xml文件

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

     @Test
       public void queryBlogIF(){
           SqlSession sqlSession = MybatisUtils.getSqlSession();
           BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
           HashMap map = new HashMap();
           map.put("tittle","Spring测试");
           List<Blog> blogs = mapper.queryBlogIF(map);
           for (Blog blog : blogs) {
               System.out.println(blog);
          }
           sqlSession.close();
      }

缓存

  • 一级缓存:只在一次sqlSession会话中有效,默认开启一级缓存

  • 二级缓存:在namespace中有效,一级缓存死掉之后把数据存到二级缓存中,同一个Mappper有效

    • 只需要在映射文件中加入一个标签

    <cache/>
    • 或者添加一些其他配置属性

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

  • 查询顺序:二级缓存->一级缓存->数据库

自定义缓存

  1. 导包

     <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
           <dependency>
               <groupId>org.mybatis.caches</groupId>
               <artifactId>mybatis-ehcache</artifactId>
               <version>1.2.3</version>
           </dependency>
  2. 自定义缓存

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  3. 编写配置文件ehcache.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
             monitoring="autodetect" dynamicConfig="true"
             updateCheck="false" name="ehcache">

       <!--mybatis-ehcache 第三方缓存:二级缓存配置文件-->

       <!--如果此处不配置updateCheck="false":则会报错
       2018-03-29 09:01:07,831 DEBUG [ehcache] net.sf.ehcache.util.UpdateChecker.checkForUpdate(UpdateChecker.java:107) - Update check failed:
       java.io.IOException: Server returned HTTP response code: 403 for URL: http://www.terracotta.org/kit/reflector?pageID=update.properties&patch=UNKNOWN&tc-product=ehcache-core+2.9.0&tc-version=2.9.0&uptime-secs=1&kitID=ehcache.default&jvm-version=1.8.0_151&os-name=Mac+OS+X&id=-1062731415&source=ehcache-core&jvm-name=Java+HotSpot%28TM%29+64-Bit+Server+VM&platform=x86_64
       at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1894) ~[?:1.8.0_151]
       at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492) ~[?:1.8.0_151]
       at net.sf.ehcache.util.UpdateChecker.getUpdateProperties(UpdateChecker.java:153) ~[ehcache-2.9.0.jar:2.9.0]
       at net.sf.ehcache.util.UpdateChecker.doCheck(UpdateChecker.java:117) ~[ehcache-2.9.0.jar:2.9.0]
       at net.sf.ehcache.util.UpdateChecker.checkForUpdate(UpdateChecker.java:104) [ehcache-2.9.0.jar:2.9.0]
       at net.sf.ehcache.util.UpdateChecker.run(UpdateChecker.java:95) [ehcache-2.9.0.jar:2.9.0]
       at java.util.TimerThread.mainLoop(Timer.java:555) [?:1.8.0_151]
       at java.util.TimerThread.run(Timer.java:505) [?:1.8.0_151]
       经研究发现,原来是ehcache捣的鬼,每次启动都会访问官方网站查看是否有最新版本,这样也可以统计有多少用户和服务器在使用ehcache。
       很可能是一个后门程序,默认开启。找到ehcache.xml文件,ehcache标签中添加属性updateCheck=”false”关闭即可。-->

       <!--
           磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存
           path:指定在硬盘上存储对象的路径
        -->
       <diskStore path="${mybatis_ehcache_disk_store}" />


       <!--
           defaultCache:默认的缓存配置信息,如果不加特殊说明,则所有对象按照此配置项处理
           maxElementsInMemory:设置了缓存的上限,最多存储多少个记录对象
           eternal:代表对象是否永不过期
           timeToIdleSeconds:最大的发呆时间
           timeToLiveSeconds:最大的存活时间
           overflowToDisk:是否允许对象被写入到磁盘
        -->
       <defaultCache maxElementsInMemory="10000" maxElementsOnDisk="10000000" eternal="false"
                     timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskExpiryThreadIntervalSeconds="120"
                     diskPersistent="true"
                     memoryStoreEvictionPolicy="LRU" />

       <!--
           cache:为指定名称的对象进行缓存的特殊配置
           name:指定对象的完整名
        -->
       <cache name="com.zbaccp.entity.Person" maxElementsInMemory="10000" eternal="false"
              timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" />


       <!--
       1.diskStore :指定数据(.data and .index)存储位置,可指定磁盘中的文件夹位置期
       2.defaultCache : 默认的管理策略
       一、以下属性是必须的:
        1、name: Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap里)。
        2、maxElementsInMemory:在内存中缓存的element的最大数目。
        3、maxElementsOnDisk:在磁盘上缓存的element的最大数目,默认值为0,表示不限制。
        4、eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。
        5、overflowToDisk: 如果内存中数据超过内存限制,是否要缓存到磁盘上。
       二、以下属性是可选的:
        1、timeToIdleSeconds: 对象空闲时间,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问。
        2、timeToLiveSeconds: 对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问。
        3、diskPersistent: 是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。
        4、diskExpiryThreadIntervalSeconds: 对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。
        5、diskSpoolBufferSizeMB: DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。
        6、memoryStoreEvictionPolicy: 如果内存中数据超过内存限制,向磁盘缓存时的策略。默认值LRU,可选FIFO、LFU。
       三、缓存的3 种清空策略 :
        1、FIFO ,first in first out (先进先出).
        2、LFU , Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。
        3、LRU ,Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。-->
    </ehcache>
posted @ 2023-07-28 17:24  仓鼠的气垫床  阅读(39)  评论(0编辑  收藏  举报