首页 > 其他 > 详细

Mybatis

时间:2021-03-18 10:04:28      阅读:30      评论:0      收藏:0      [点我收藏+]

1. 简介

什么是Mybatis

官方文档:https://mybatis.org/mybatis-3/zh/index.html

持久层解释

  • 持久化就是将数据在持久化状态和瞬时状态转化的过程
  • 持久化就是保存的意思
  • 持久层就是完成持久化的代码

2 第一个Mybatis项目

2.1 创建一个普通的maven项目,配置依赖

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.3</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.13</version>
    </dependency>
</dependencies>
    <properties>

                <maven.compiler.source>13</maven.compiler.source>

                <maven.compiler.target>13</maven.compiler.target>

    </properties>

2.2 编写核心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://localhost:3306/student?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

2.3 编写mybatis工具类

public class mybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            //1.获取sqlsessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    //2.可以执行crud的对象
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
}

2.4 编写代码

  • 实体类

  • //实体类
    public class user {
        private int id;
    private String username;
    private String pwd;
    
        public user() {
        }
    
        public user(int id, String username, String pwd) {
            this.id = id;
            this.username = username;
            this.pwd = pwd;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    
        @Override
        public String toString() {
            return "user{" +
                    "id=" + id +
                    ", username=‘" + username + ‘\‘‘ +
                    ", pwd=‘" + pwd + ‘\‘‘ +
                    ‘}‘;
        }
    }
    
  • 接口

    public interface userDao {
        List<user> getUserList();
    }
    
  • 设置对应的mapping配置文件

    <?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 namespace="com.sun.dao.userDao">
        <!--select查询语句-->
        <select id="getUserList" resultType="com.sun.pojo.user">
            select * from student.user
        </select>
    </mapper>
    

    2.4 测试

    编写一个测试类

    public class UserDao_Test {
        @Test
        public void test(){
            SqlSession sqlSession= mybatisUtils.getSqlSession();
    
            UserDao userDao=sqlSession.getMapper(UserDao.class);
            List<User> userList=userDao.getUserList();
            for(User user : userList)
            {
                System.out.println(user);
            }
        }
    }
    

    注册mapping

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

    处理找不到文件的问题

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

    2.5 步骤总结

    1. 写一个工具类
    2. 写一个mybatis-config.xml配置文件
    3. 写一个实体类
    4. 写接口
    5. 写mapping.xml配置
    6. 写测试类

3. CRUD

3.1 基本语法

  1. namespace

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

  2. select

    • id:就是对应的namespace中的方法名一致
    • resulttype:sql语句的返回值!
    • parameterType:参数的类型!

    步骤:

    1. 编写接口

      List<User> getUserList();
      
    2. 编写对应的mapper中的sql语句

          <select id="getUserList" resultType="com.sun.pojo.User">
              select * from student.user
          </select>
      
    3. 测试

      @Test
      public void test(){
          //第一步,获得sqlSession对象
          SqlSession sqlSession= mybatisUtils.getSqlSession();
      
          try {
              UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
              List<User> userList= userMapper.getUserList();
              for(User user : userList)
              {
                  System.out.println(user);
              }
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              //关闭sqlSession
              sqlSession.close();
          }
      }
      

注意:增删改需要 提交事务!

3.2 查找

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

3.3 添加

<select id="insertUser" parameterType="com.sun.pojo.User">
    insert into student.user (id,username ,pwd) values (#{id},#{username},#{pwd})
</select>

3.4 修改

<update id="updateUser" parameterType="com.sun.pojo.User">
    update student.user set username=#{username},pwd=#{pwd}    where  id=#{id};
</update>

3.5 删除

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

3.6 Map

map传递参数,直接在SQL中取出key即可!

parameterType="map"

对象传递参数,直接在SQL中取对象的属性即可 !

parameterType="com.sun.pojo.User">

只有一个基本类型参数的情况下,可以直接在SQL中取到!

多个参数用map或注解!

3.7 模糊查询

  1. java代码在执行的时候,传递通配符%%
  2. 在sql中拼接使用通配符!

4 配置文件

4.1 属性

1 可以用db.properties方法来优化

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/student?useSSL=true&useUnicode=true
username=root
password=123
<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>

2 优先级问题

如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:

  • 在 properties 元素体内指定的属性首先被读取。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。

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

4. 2 类型别名(typeAliases)

类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余

第一种:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

第二种:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

在实体类比较少的时候,使用第一类;

如果实体类十分多,建议使用第二类;

第一章可以DIY(自定义),第二种则不行,如果非要改,可以在实体中增加注解

4.3 设置(setting)

4.4 mapping(映射器)

注册绑定xml

每一个Mapper.xml都必须在Mybatis的核心配置文件下进行注册

  1. 方法一:通过xml【推荐使用】

    <mappers>
        <mapper resource="com/sun/dao/UserMapper.xml"></mapper>
    </mappers>
    
  2. 方式二:使用class文件注册

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

注意:

  • 接口和他的mapper配置文件必须同名!
  • 接口和他的mappper配置文件必须在一个包下!
  1. 方法三:

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

    注意:

    • 接口和他的mapper配置文件必须同名!
    • 接口和他的mappper配置文件必须在一个包下!

4.5 生命周期和作用域

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

技术分享图片

SqlSessionFactoryBuilder

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

SqlSessionFactory

  • 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
  • SqlSessionFactory 的最佳作用域是应用作用域。
  • 最简单的就是使用单例模式或者静态单例模式。
  • 可以理解为连接池

SqlSession

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

技术分享图片这里的每一个Mapper都代表一个具体的业务!

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

1. 方法:

原来这样的sql语句

select * from student.user where id=#{id}

如果直接执行,就会出现错误,对应的属性值可能为空

改成这样即可,起别名

select id,username,pwd as password from student.user where id=#{id}
id,username,pwd就是数据库中的字段名
password是pwd对应的属性名

2. 结果集映射resultMap

id username pwd                   数据库中的字段名
id username password              对应的属性名

技术分享图片

  • resultMap是MyBatis中最重要强大的元素
  • ResultMap的设计思想是,对于简单的语句根本不需要显示配置结果映射,而对于复杂的语句,只描述他们的关系就行了。
  • 一样的就可以不用映射了

6. 日志

  1. 日志工厂

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

    • SLF4J
    • LOG4J (掌握)
    • LOG4J2
    • JDK_LOGGING
    • COMMONS_LOGGING
    • STDOUT_LOGGING (掌握)
    • NO_LOGGING

技术分享图片

注意:value后面小心空格!

  1. LOG4j

    • 导入jar包

    • 配置log4j.properties (新建以这个为名)这个配置文件

    • 配置log4j为日志的实现

      <settings>
          <setting name="logImpl" value="LOG4j"/>
      </settings>
      
    • 测试

    技术分享图片

7. 分页

  1. 接口

    List<User> getUserbyLimit(Map<String,Object> map);
    
  2. Mapper.xml

    <select id="getUserbyLimit" parameterType="map" resultType="com.sun.pojo.User">
        select * from student.user limit #{startIndex},#{pageslll}
    </select>
    
  3. 测试

    @Test
    public  void getUserbyLimit(){
        SqlSession sqlSession = null;
        try {
            sqlSession= mybatisUtils.getSqlSession();
            UserMapper mapper= sqlSession.getMapper(UserMapper.class);
            Map<String,Object> map=new HashMap<String, Object>();
    
            map.put("startIndex",0);
            map.put("pageslll",2);
    
            List<User> userList= mapper.getUserbyLimit(map);
            for (User user : userList) {
                System.out.println(user);
            }
            ;
        } catch (Exception e) {
            e.printStackTrace();
        }
        sqlSession.close();
    }
    

8. 注解开发

1. 面向接口编程

2. 使用注解开发

技术分享图片

本质:反射机制体现!

底层:动态代理!

3. CRUD

查询

  1. 接口实现

    //方法存在多个参数,一定要用@Param,方法中的参数和@Param中的参数相比,以@Param中的参数为主
    @Select("select * from user where id=#{id}")
    User getiUserID(@Param("id") int id);
    
  2. 核心配置文件绑定接口

    <mappers>
        <mapper class="com.sun.dao.UserMapper"></mapper>
    </mappers>
    
  3. 测试

        //注解开发
        @Test
        public void getiUserID(){
            SqlSession sqlSession=mybatisUtils.getSqlSession();
    
            UserMapper mapper=sqlSession.getMapper(UserMapper.class);
    
            User user=mapper.getiUserID(1);
            System.out.println(user);
            sqlSession.close();
        }
    

添加

  1. 接口实现

    @Insert("insert into user(id,username,pwd) values(#{id},#{username},#{pwd})")
    int insertUserBy(User user);
    
  2. 如查询的步骤二一样,绑定过一次就不用每次都绑定了!

  3. 测试

        @Test
        public void insertUserBy(){
            SqlSession sqlSession=mybatisUtils.getSqlSession();
    
            UserMapper mapper=sqlSession.getMapper(UserMapper.class);
    
            mapper.insertUserBy(new User(22,"李红","890"));
            sqlSession.close();
    
        }
    

    注意:正常情况下,增删改都需要提交事务才能够真正修改数据库!现在不用,是因为设置了自动提交事务!

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

修改

  1. 接口实现

    @Update("update user set username=#{username},pwd=#{pwd} where id=#{id}")
    int updateByzhujie(User user);
    
  2. 如查询的步骤二一样,绑定过一次就不用每次都绑定了!

  3. 测试

        @Test
        public void insertUserBy(){
            SqlSession sqlSession=mybatisUtils.getSqlSession();
    
            UserMapper mapper=sqlSession.getMapper(UserMapper.class);
    
            mapper.updateByzhujie(new User(9,"时代","457"));
    
            sqlSession.close();
        }
    

删除

  1. 接口实现

    @Delete("delete from user where id=#{id}")
    int deleteByzhujie(int id);
    
  2. 如查询的步骤二一样,绑定过一次就不用每次都绑定了!

  3. 测试

        @Test
        public void insertUserBy(){
            SqlSession sqlSession=mybatisUtils.getSqlSession();
    
            UserMapper mapper=sqlSession.getMapper(UserMapper.class);
    
            mapper.deleteByzhujie(22);
            sqlSession.close();
        }
    

关于@Param注解

  • 基本类型的参数或者string类型,需要加上
  • 引用类型不用加上
  • 如果只有一个基本类型参数,可以不用!
  • 我们在SQL中引用的就是这里的@param设置的属性名!

9. Lombok

目的是为了简化开发,它是个java库,是个构建工具,也是个插件

9.1 步骤

  1. idea中加入插件

    技术分享图片

  2. 在项目中导入jar包

  3. 在实体类中加注解即可

    @Getter and @Setter
    @FieldNameConstants
    @ToString
    @EqualsAndHashCode
    @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
    @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
    @Data
    @Builder
    @SuperBuilder
    @Singular
    @Delegate
    @Value
    @Accessors
    @Wither
    @With
    @SneakyThrows
    @val
    @var
    experimental @var
    @UtilityClass
    Lombok config system
    Code inspections
    Refactoring actions (lombok and delombok)

10. 多对一

多个学生,一个老师

  • 对于学生而言,一个老师关联多个学生
  • 对于老师而言,集合,一个老师管理多个学生

按照查询嵌套处理

技术分享图片

按照结果嵌套处理

技术分享图片

11. 一对多

环境搭建

实体类

技术分享图片

技术分享图片

按照结果集查询

技术分享图片

按照查询嵌套处理

技术分享图片

小结

  • 关联-associate 【多对一】
  • 集合-collection· 【一对多】
  • javaType&ofType
    1. javaType用来指定实体类中属性的类型
    2. ofType 用来指定映射到list或者集合中的pojo类型,泛型中的约束类型!

12. 动态SQL

定义:自动生成SQL语句

if

<select id="queryByIF" resultType="com.sun.pojo.User" parameterType="map">
    select * from student.user where 1=1
    <if test="id!=null">
        and id=#{id}
    </if>
</select>

注意:resultType中应该写成com.sun.pojo.User,而不是简单的User就可以了!

where

<select id="queryByIF" resultType="com.sun.pojo.User" parameterType="map">
    select * from student.user
    <where>
        <if test="id!=null">
            and id=#{id}
        </if>
    </where>
</select>

注意:where主要是可以自动添加或删除and,可以一个条件成立,也可以多个条件成立

trim, where, set

set 的作用就是只能选择一个条件成立

trim:定制 where 元素的功能

foreach

SQL片段

技术分享图片注意事项:

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

13. 缓存

13.1 什么样的数据适合缓存?

经常查询但是不经常改变的数据

Mybatis中默认有两个缓存,一级缓存和二级缓存

  • 一级缓存就是本地缓存,默认开启,sqlsession级别的缓存
  • 二级缓存需要手动配置启动,是基于namespace级别的缓存
  • 有缓存接口cache,我们可以通过cache自定义二级缓存

13.2 一级缓存

  • 与数据库同一次会话期间查询的数据会放在本地缓存中
  • 以后如果需要相同的信息,直接从缓存中获取,没有必要再去查询数据库

测试步骤:

  1. 开启日志

  2. 测试在一个session中查询两次相同记录

  3. 查看日志,分析

    通过id查询两次id=1的数据

    技术分享图片

    **通过查看日志可以看出SQL·语句执行了一次,第二次直接从缓冲中获取了

    • 查看id=1和id=2的数据

      技术分享图片

      可以发现,SQL语句执行了两次!因为这是不同的数据,查询过id=1的数据之后,缓存中并没有id=2的数据,因此需要再次查询数据库

    缓存失效:

    1. 查询不同的东西
    2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
    3. 查询不同的mapper.xml
    4. 手动清理缓存

    小结:一次缓存默认是开启的,只在一次sqlsession中有效,也就是拿到连接到关闭连接的区间段!一级缓存就是一个mapper!

13.3 二级缓存

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

步骤:

  1. 显示开启全局缓存( 默认就是开启的,现在只是显示出来而已)

        <settings>
    <!--        显示的开启全局缓存-->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    
  2. 在一个mapper中开启二级缓存

    <!--    在当前maper中开启二级缓存-->
        <cache/>
    

也可以自定义参数

<cache eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>
  1. 测试

    1. 问题是我们需要将实体类序列化!否则就会报错!

      技术分享图片

改为技术分享图片

小结:

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

13.4 缓存原理

缓存顺序:

  1. 用户先查询二级缓存
  2. 接着查询一级缓存
  3. 缓存中没有的话,从数据库中查询,放在一级缓存中
  4. 会话关闭的话,数据从一级缓存放到二级缓存中

13.5 自定义缓存-ehcahe

ehcache是一种广泛使用的开源java分布式缓存,主要用于通用缓存

要在程序中使用

Mybatis

原文:https://www.cnblogs.com/shyshare/p/14553529.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!