mybatis详解----有这一篇就够了!!!

mybatis 专栏收录该内容
2 篇文章 0 订阅

mybatis

1. 简介

1.1 mybatis概述

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis 。2013年11月迁移到Github

iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)

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

帮助文档

使用其,需要引入依赖

(x.x.x 指版本 在maven仓库寻找合适版本)

maven

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

1.2 JDBC

//利用jdbc,完成新增的功能
    private static void method2() throws Exception{
        //1,注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2,获取数据库的连接
        //数据传输协议   数据库的ip 端口号 数据库名
        String url = "jdbc:mysql://localhost:3306/lxb";
        Connection c = DriverManager.getConnection(url,"root","root");
        //3,获取传输器
        Statement s = c.createStatement();
        //4,利用传输器执行  增删改的SQL
        //executeUpdate()用来执行增删改的SQL,只返回影响行数
        int rows = s.executeUpdate(
                "INSERT INTO emp(ename,job) VALUES('rose','副总')");
        //5,释放资源
        //r.close();//结果集
        s.close();//传输器
        c.close();//连接
    }

在没使用mybatis时,我们一般用JDBC连接数据库进行持久化操作。
但太过繁琐,开发成本高,还需手动关闭,所以这些繁琐的代码被封装在mybatis使开发者只需要关注 SQL 本身,但在代码执行效率上JDBC比mybatis快。

1.3 ORM

对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。如今已有很多免费和付费的ORM产品,而有些程序员更倾向于创建自己的ORM工具。
延伸:
1. 对象与数据库中的表一一对应
2. 对象的属性与表中的字段一一对应
3. 其中的映射应该由程序自动完成.无需人为干预.
4. mybatis基于ORM设计思想实现了以对象的方式操作数据库
了解:
········mybatis的ORM不完整,只实现了结果集的映射。sql语句还需自己写,所以mybatis也被称为半自动化ORM框架。

1.4 持久层

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程(即将内存数据保存在硬盘中)
  • 内存 :断电及失
  • 数据库(jdbc)io文件持久化
  • 持久层:程序通过Dao/Mapper 与数据库进行交互的层级代码(Controller层 Service层 Dao/Mapper层)

为什么要有持久化?

​ 因为内存有断电及失的特性。但有些对象我们不能将其丢失,所有出现持久化。

2 第一个mybatis程序

思路

  • 搭建环境
  • 导入mybatis
  • 编写代码
  • 测试

2.1 搭建环境

搭建数据库

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

创建maven项目

导入依赖(版本号可以去 MVN 进行查找)

<!--mybatis依赖包-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>

<!--mysql依赖包-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
</dependency>

2.2 导入mybatis配置文件

编写mybatis的核心配置文件 在resources中创建xml文件 mybatis-config

XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)

<?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="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
 <!-- 导入mapper.xml配置文件,里面配置了SQL语句和DAO接口的映射关系,每个map都要在核心配置文件注册-->
    <mappers>
        <mapper resource="mapper.xml"/>
    </mappers>
</configuration>

在resource创建mapper包,在此包中根据需要创建与dao成对应的XXXmapper.xml文件

<?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">
<!--SQL语句配置文件,需要在mybatis配置文件中进行加载-->

   <!--mapper标签用于做SQL语句和方法之间的映射-->

   <!--一个mapper对应一个类,namespace里面写的是dao接口的完整路径-->

   <mapper namespace="com.nuc.dao.UserDao">

       <!--select的id是方法名-->

       <!--resultType是查询的结果的一种映射,User类的属性与列名完全一致(bean类),所以查询的结果会转换成User类-->

       <select id="getAll" resultType="User类的完整路径:包名.类名">

           /具体的sql语句/

           select * from usertest

       </select>

   </mapper>

2.3 编码

pojo层

写入一个user类

dao层----与mapper对应 要在namespace写入对应全类名

import com.nuc.pojo.User;
 
import java.util.List;
 
public interface UserDao {
    List<User> getAll();

Service层

ServiceImpl层

2.4 测试

public class Test {
    public static void main(String[] args) throws Exception {
        //加载配置文件
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        //得到sqlSessionFactoryBuilder
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = builder.build(reader);

        //得到sqlSession
        SqlSession sqlSession = build.openSession();
        //操作sql  传入参数:类名.方法名
        List<User> list = sqlSession.selectList("com.nuc.dao.UserDao.getAll");
        //遍历
        for (User u : list){
            System.out.println(u);
        }

        //关闭资源
        sqlSession.close();
        reader.close();
    }
}

3 crud(增删改查)

1 insert

增加语句;

  • id:就是对应的namespace中的方法名;
  • resultType : Sql语句执行的返回值;
  • parameterType : 参数类型;
<!--namespace=绑定一个指定的Dao/Mapper接口-->
<mapper namespace="com.nuc.dao.UserDao">
    <select id="getAll" resultType="com.nuc.pojo.User" >
        select * from USER
    </select>
</mapper>

​ 步骤 :

  1. 编写接口
public interface UserMapper {
    //插入用户
    public void addUser(User user);
}
  1. 编写对应的mapper中的sql语句

    <insert id="addUser" parameterType="com.nuc.pojo.User">
        insert into user (id,name,password) values (#{id}, #{name}, #{password})
    </insert>
    
    3. 测试
    
    @Test
    public void test2() {
       SqlSession sqlSession = MybatisUtils.getSqlSession();
       UserMapper mapper = sqlSession.getMapper(UserMapper.class);
       User user  = new User(1,"望埠","123");
       mapper.addUser(user);
       //增删改一定要提交事务
       sqlSession.commit();
    
       //关闭sqlSession
       sqlSession.close();
    }
    
    

2. select

   <select id="getAll" resultType="com.nuc.pojo.User">
        select * from USER
    </select>

3. update

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

4. Delete

<delete id="delectUser" parameterType="java.lang.String">
        delete from user where name = #{name}
    </delete>

注意 增删改一定要提交事务:sqlSession.commit();

转义字符

在这里插入图片描述
上述会报错,会将其识别为标签符号。我们可以用转义字符
在XML中,需要转义的字符有:
  (1)&   &amp;
  (2)<   &lt;
  (3)>   &gt;
  (4)"   &quot; 
  (5)'  &apos; 
  例如:

select * from demo_user where age>=#{minAge} and age&lt;#{maxAge}

如果sql中有大量转义字符,建议使用转义标签
语法: <![CDATA[ XXX内容 报文 ]]>

 <![CDATA[ select * from demo_user where age>=#{minAge} and age<#{maxAge}]]>

Map

当我们的实体类,或数据库中的表参数过多,我们应该使用map

 <insert id="addUser2" parameterType="map">
        insert into user (id,name)
        values (#{userid},#{username});
    </insert>

测试:

    @Test
    public void test02() throws IOException {
        //加载配置文件
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        //得到sqlSessionFactoryBuilder
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = builder.build(reader);

        //得到sqlSession
        SqlSession sqlSession = build.openSession();
        Map<String,Object> map = new HashMap<>();
        map.put("userid",5);
        map.put("username", "jj");
        sqlSession.insert("com.nuc.dao.UserDao.addUser2",map);
        sqlSession.commit();
//关闭sqlSession
        sqlSession.close();
    }

注意 :提交事务

Map传递参数,直接在sql中取出key即可! 【parameter=“map”】

对象传递参数,直接在sql中取出对象的属性即可! 【parameter=“Object”】

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

多个参数用Map , 或者注解!

集合查询

集合参数写法 list/mapper/array
如:查询集合内的id对应的信息
语法:

  • collection 集合 如果传入为数组 用array 传入List 用list 当为map可以是传入的参数名
  • item 每次遍历数据的形参变量。
  • index 循环遍历下标,一般不用 可省略
  • open 循环开始前加的符号 一般为“(”
  • separator 循环遍历的分隔符
  • close 循环结束后加的符号 一般为“)”
  1. 先编写接口
 List<DemoUser> findList(List<Integer> list);
  1. mapper-----注意中文符号问题,我找了半天没发现 😦
  <select id="findList" resultType="com.demo1.pojo.DemoUser" parameterType="java.util.List">
        select * from demo_user where id in <foreach collection="list" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>

模糊查询

1 在java代码执行时,传递通配符

接口:

List<User> selectlike(String value);

mapper

<select id="selectlike" resultType="com.nuc.pojo.User" parameterType="java.lang.String">
        select * from user where name like #{value}
    </select>

测试:

@Test
public void test() throws IOException {

    //加载配置文件
    Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
    //得到sqlSessionFactoryBuilder
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory build = builder.build(reader);

    //得到sqlSession
    SqlSession sqlSession = build.openSession();
    List<User> list = sqlSession.selectList("com.nuc.dao.UserDao.selectlike","%李%");

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

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

2.在sql拼接使用通配符

<select id="selectlike" resultType="com.nuc.pojo.User" parameterType="java.lang.String">
        select * from user where name like “%”#{value}“%”
    </select>

mybatis的xml配置

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

configuration(配置)

1.环境配置environments

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

<environments default="development">
    <!--通过default  来选择需要的环境的id来控制环境-->
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <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.事务管理器(transactionManager)

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

3.数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")

mybatis默认事务管理器jdbc 连接池 POOLED

4. 属性(properties)

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

步骤:

  1. 可以在resource中创建db.properties文件,用于存放属性
driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&appserverTimezone=UTC
username =root
password = root
2. 在核心配置文件映入
 <!--引入外部配置文件-->   
<properties resource="db.properties"></properties>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
  • 可以直接引入外部文件
  • 可以在其中增加一些属性配置
  • 如果两个文件有同一个字段,优先使用外部配置文件的

5. 类型别名(typeAliases)

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

<!--给实体类起名-->
<typeAliases>
    <typeAlias type="com.nuc.pojo.User" alias="user"></typeAlias>
</typeAliases>

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

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

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

但若在实体类上有注解,则别名为其注解值。见下面的例子:

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

6设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

设置setting详情

7.映射器(mappers)

MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。

现在主要用以下方法实现

第一种

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="com/nuc/Mapper/UserMapper.xml"/>
  <mapper resource="com/nuc/Mapper/PersonMapper.xml"/>
 
</mappers>

第二种

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

第三种

<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
    <mapper class="com.kuang.dao.UserMapper"/>
</mappers>

**注意点:**第二种和第三种方法

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

8.生命周期和作用域

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

SqlSessionFactoryBuilder:

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

  • 局部变量

    SqlSessionFactory:

  • 说白了就可以想象为:数据库连接池
    SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建一个实例。因此SqlSessionFactory的最佳作用域是应用作用域(ApplocationContext)。

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

    SqlSession:

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

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

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

解决用户属性名与数据库字段名不匹配问题

当累的属性为:

 private int id;
    private String name;
    private String password;

数据库字段名为

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

用User接收无法接收到password属性

解决 方法:

  1. 起别名

     <select id="getAll" resultType="aaa">
            select id,name,pwd as password from USER
        </select>
    
  2. 使用resultMap

resultMap是一个结果集映射

<!--结果集映射-->
<resultMap id="UserMap" type="User">
    <!--column数据库中的字段,property实体类中的属性-->
   <!-- 相同的可以不去写,只用关注不同的
<result column="id" property="id"></result>
<result column="name" property="name"></result>-->
    <result column="pwd" property="password"></result>
</resultMap>

<select id="getUserList" resultMap="UserMap">
    select* from USER
</select>
  • resultMap 元素是 MyBatis 中最重要最强大的元素。

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

  • ResultMap 的优秀之处——你完全可以不用显式地配置它们。

  • 如果这个世界总是这么简单就好了(尤其是找对象)

日志

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

曾经:sout输出、debug

现在:日志工厂

  • SLF4J
  • **LOG4J ** (使用要导包)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING
  • NO_LOGGING

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

STDOUT_LOGGING

<!-- 标准的日志工厂实现-->
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

注意 是logImpl ,Impl的i是大写的,不能有一点错,也不能有空格。

log4j

  1. 简介:

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

  1. 使用

    2.1 导包

    地址

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

    在resources中配置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.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
    #文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/lxb.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
    
    1. 3在核心配置文件配置
    <settings>
            <setting name="logImpl" value="LOG4J"/>
        </settings>
    

    2.4 在要使用log4j的類中使用

    import org.apache.log4j.Logger;
    import org.junit.jupiter.api.Test;
    
    public class UserTest {
        static Logger logger = Logger.getLogger(UserTest.class);  //括號内跟本类的。class
        @Test
        public void test(){
            logger.info("info: 测试log4j");
            logger.debug("debug: 测试log4j");
            logger.error("error:测试log4j");
        }
    }
    
    

    运行完后生成log4j文件

分页

1. 使用Limit分页

SELECT * from user limit startIndex,pageSize 

下面是每页显示4个 从0开始查

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

# 相当于SELECT * from user limit 0,3

SELECT * from user limit 3

2. 使用rowbounds分页

不再使用SQL实现分页,通过java代码实现

接口

List<User> getUserByRowBounds();

mapper

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

测试

@Test   
public void getUserByRowBounds(){
         //加载配置文件
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            //得到sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = builder.build(reader);

            //得到sqlSession
            SqlSession sqlSession = build.openSession();
        //RowBounds实现  从下表一开始,每页显示2个
        RowBounds rowBounds = new RowBounds(1, 2);
        //通过Java代码层面实现分页
        List<User> userList = sqlSession.selectList("com.kaung.dao.UserMapper.getUserByRowBounds", null, rowBounds);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

3.通过插件

mybatis分页插件pageHelper

链接

使用说明文档

说明文档

使用注解开发

面向接口开发

三个面向区别

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性和方法;
  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现;
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构;
  1. 注解直接写在接口内
public interface UserDao {
       @select("select * from user")
    List<User> getAll();
}

​ 2. 在核心配置文件绑定接口

<mappers>
       <!-- <mapper resource="mapper.xml"></mapper>-->
        <mapper class="com.nuc.dao.UserDao"></mapper>
    </mappers>
  1. 测试
   //加载配置文件
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        //得到sqlSessionFactoryBuilder
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = builder.build(reader);

        //得到sqlSession
        SqlSession sqlSession = build.openSession();
       UserDao mapper= sqlSession.getMapper(UserDao.class);
       List<User> list = mapper.getAll();

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

        //关闭sqlSession
        sqlSession.close();

本质:反射机制实现

底层:动态代理

注解crud

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

//方法存在多个参数,所有的参数前面必须加上@Param("id")注解
@Delete("delete from user where id = ${uid}")
int deleteUser(@Param("uid") int id);

mybatis的详细执行流程

在这里插入图片描述

Lombok

Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法。官方地址:github地址:

idea使用步骤

  1. 在setting中的plugins中搜索安装LomBok插件
  2. 导入lombok的jar包
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>

能使用的注解

@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

@Data 生成无参构造 set和get方法 hashcode,equals 和tostring

@AllArgsConstructor 生成所有的有参构造,但会失去默认生成的无参构造,要与@NoArgsConstructor(生成无参构造)配合

多对一

一个老师关联多个学生------一对多

多个学生对应一个老师-------多对一

在数据库建表
在这里插入图片描述

查询结果在mapper中较复杂,用高级结果映射

结果映射(resultMap)

  • constructor

    - 用于在实例化类时,注入结果到构造方法中

    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能

    • arg - 将被注入到构造方法的一个普通结果

  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

  • result – 注入到字段或 JavaBean 属性的普通结果

  • association

    – 一个复杂类型的关联;许多结果将包装成这种类型

    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection

    一个复杂类型的集合

    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator使用结果值来决定使用哪个resultMap

    • case

      基于某些值的结果映射

      嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

<!--比如学生表里有学生id,姓名,老师id   查出其信息   老师应显示name-->
@Data
public class Student {
    private int id;
    private String name;
    private int tid;

    //学生需要关联一个老师
    private Teacher teacher;
}

1.按照查询嵌套处理

<!--
     思路:
        1. 查询所有的学生信息
        2. 根据查询出来的学生的tid寻找特定的老师 (子查询)
    -->
<select id="getStudent" resultMap="StudentTeacher">
    select * from student
</select>
<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>
<select id="getTeacher" resultType="teacher">
    select * from teacher where id = #{id}
</select>
  1. 按照结果嵌套处理
  <!--按照结果进行查询-->
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid , s.name sname, t.name tname
        from student s,teacher t
        where s.tid=t.id
    </select>
    <!--结果封装,将查询出来的列封装到对象属性中-->
    <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>
        </association>
    </resultMap>

mysql中多对一的查询方式:子查询,联表查询

一对多

同样的例子,但环境 搭建是teacher有所改变------查询指定老师下的老师及学生信息

@Data
public class Teacher {
    private int id;
    private String name;

    //一个老师拥有多个学生
    private List<Student> students;
}
<!--按结果嵌套查询-->
<select id="getTeacher" resultMap="StudentTeacher">
    SELECT s.id sid, s.name sname,t.name tname,t.id tid FROM student s, teacher t
    WHERE s.tid = t.id AND tid = #{tid}
</select>
<resultMap id="StudentTeacher" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!--复杂的属性,我们需要单独处理 对象:association 集合:collection
    javaType=""指定属性的类型!
    集合中的泛型信息,我们使用ofType获取
    -->
    <collection property="students" ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>  
<!--按照查询嵌套-->
<selectid="getTeacher2" resultMap="StudentTeacher2">
    SELECT * from teacher where id=#{id}</select>
<resultMap id="StudentTeacher2" type="Teacher">
     	<result property="id" column="sid"/>
        <result property="name" column="sname"/>
   <!--上面二行可省略不写-->
   <collection  property="students" javatype="ArrayList"oftype="Student"
               select="studentbytid " column="id">
    </collection>
</resultMap>
<select id="studentbytid"  resultType="Student">
	select * from student where tid=#{id}
</select>

动态sql

if----用以拼接sql:

<select id="queryBlogIF" 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>

where where标签当底下的条件都不成立不会被执行,当第一个有and或or会自动去掉

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

choose 有时候我们不想用到所有的条件语句,而只是想选其中的一项,有点像switch

包含when标签,当XXX(条件)执行(语句)

当when都不成立,执行otherwise里的语句

<select id="queryBlogchoose" parameterType="map" resultType="blog">
    select * from blog
    <where>
       <when test="title!=null">
        and title = #{title}</when>
        <when test="author!=null">
        and author = #{author}</when>
        <otherwise>
        and views=#{views}</otherwise>
    </where>
</select>

set 类似于 解决动态更新的操作,会自动删除无关的逗号

<update id="updatedata" paramterType="map">
    update blog
    <set>
    <if test="title!=null">
             title = #{title},
        </if>
        <if test="author!=null">
            author = #{author}</set>
        where id=#{id}
</update>

trim 用于去掉指定前缀后缀(一般不用)

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


<trim prefix="SET" suffixOverrides=",">
<if test="title=null&author!=null">
    title,</if>
</trim>

sql片段

用于提高代码复用

<!--使用sql抽取公共部分-->
<sql id="if-title-author"> 
<if test="title!=null">
            and title = #{title}
        </if>
        <if test="author!=null">
            and author = #{author}
        </if>
</sql>

<!--在需使用的地方使用include调用-->
<select id="queryBlogwhere" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

foreach
如查询集合内id的信息

List<User> findList(ArrayList list);
List<DemoUser> findList01( List<Integer> list);

用于遍历

<select>select * from User where  in
<foreach  item="id" collection="Array" open="[" separator="," close="]">
    #{id}
</foreach></select>

 <select id="findList01" resultType="com.demo1.pojo.DemoUser" parameterType="java.util.List">
        select * from demo_user where id in <foreach collection="list" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
    </select>

缓存

查询连接数据库非常消耗资源

一次查询的结果我们可以将其暂存在一个可直接取到的地方-----内存:缓存

我们再次查询相同的数据,直接走缓存

  1. 什么是缓存(Cache)?
    • 存在内存的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户查询数据就不用去磁盘(关系型数据库数据文件)传讯,提高效率,解决高并发系统的性能问题。

Mybatis缓存

mybatis系统默认定义了两级缓存:一级缓存和二级缓存

  • 一级缓存

    默认开启,同一个SqlSesion级别共享的缓存,在一个SqlSession的生命周期内,执行2次相同的SQL查询,则第二次SQL查询会直接取缓存的数据,而不走数据库,当然,若第一次和第二次相同的SQL查询之间,执行了DML(INSERT/UPDATE/DELETE),则一级缓存会被清空,第二次查询相同SQL仍然会走数据库

    ​ 一级缓存在下面情况会被清除

    1. 在同一个SqlSession下执行增删改操作时(不必提交),会清除一级缓存
    2. SqlSession提交或关闭时(关闭时会自动提交),会清除一级缓存
    3. 对mapper.xml中的某个CRUD标签,设置属性flushCache=true,这样会导致该MappedStatement的一级缓存,二级缓存都失效(一个CRUD标签在mybatis中会被封装成一个MappedStatement)
    4. 在全局配置文件中设置 <setting name="localCacheScope" value="STATEMENT"/>,这样会使一级缓存失效,二级缓存不受影响

测试一级缓存:打开日志

缓存失效:

  1. 查询不同的资源
  2. 增删改操作,可能会改变原数据,刷新缓存
  3. 查询不同的mapper
  4. 手动清理(.clearCache())
  • 二级缓存(也叫全局缓存)

    默认关闭,可通过全局配置文件中的开启二级缓存总开关,然后在某个具体的mapper.xml中增加

    工作机制:

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;

    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;

    • 新的会话查询信息,就可以从二级缓存中获取内容;

    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

    步骤:

    1. 在mybatis-confi。xml文件开启
    <setting name="cacheEnabled" value="true"/>
    
    
    1. 在具体要开启的mapper。xml中打开它
    <cache/>
    
    
    

    也可以对其进行配置具体见说明文档帮助文档

    <cache
     eviction="FIFO"
     flushInterval="60000"
     size="512"
     readOnly="true"/>
    <!--这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。-->
    <!--也可在具體的查詢语句中开启-->
    <select id="updateUser" parameterType="int" useCache="true">
      select * from user where id = #{id}
    </select>
    

    Ehcache

    Ehcache是一种广泛使用的java分布式缓存,用于通用缓存(现在流行用redis)

    使用步骤:

    ​ 1. 要在应用程序中使用Ehcache,需要引入依赖的jar包


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

		2. 在mapper使用对应的缓存
<mapper namespace = “org.acme.FooMapper” > 
   <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> 
</mapper>
  1. 编写ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
        updateCheck="false">
   <!--
      diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
      user.home – 用户主目录
      user.dir – 用户当前工作目录
      java.io.tmpdir – 默认临时文件路径
    -->
   <diskStore path="./tmpdir/Tmp_EhCache"/>
   
   <defaultCache
           eternal="false"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="259200"
           memoryStoreEvictionPolicy="LRU"/>

   <cache
           name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>
   <!--
      defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
    -->
   <!--
     name:缓存名称。
     maxElementsInMemory:缓存最大数目
     maxElementsOnDisk:硬盘最大缓存个数。
     eternal:对象是否永久有效,一但设置了,timeout将不起作用。
     overflowToDisk:是否保存到磁盘,当系统当机时
     timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
     timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
     diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
     diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
     diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
     memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
     clearOnFlush:内存数量最大时是否清除。
     memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
     FIFO,first in first out,这个是大家最熟的,先进先出。
     LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
     LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
  -->
</ehcache>
  • 5
    点赞
  • 1
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值