SSM框架专题-MyBatis框架从零入门老杜版笔记(上)

植被类型分布

MyBatis框架从零入门老杜版笔记(上)

Numpy学习

一、MyBatis概述

1.1 框架 framework

在这里插入图片描述

系统架构

1.2 MyBatis和JDBC的关系

MyBatis是增强版的jdbc,把一些功能通过反射封装好了

三维

1.3 JDBC不足

  • sql语句写死,如果拓展功能,可能整个sql语句作废
  • set操作繁琐,如果sql语句有一百个?可能要写一百个set

1.4 了解MyBatis

  • MyBatis是对jdbc的封装
  • MyBatis之前叫做ibatis,后来才改名
  • MyBatis是持久层框架

1.5 了解ORM

  • O(Object):JVM中的java对象
  • R(Relational):关系型数据库
  • M(Mapping):映射
  • 什么是ORM?:JavaBean与数据表记录的互相映射
  • 在这里插入图片描述

二、MyBatis入门程序

2.1 建数据库表

在这里插入图片描述
添加两条字段
在这里插入图片描述

分布式账本

2.2 加载mybatis的五个步骤

  1. 配置Maven环境,打包方式设置为jar,加载MyBatis,MySQL
<packaging>jar</packaging>
<dependencies>
        <!--  MyBatis  -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.11</version>
        </dependency>

        <!--  MySQL  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

    </dependencies>
  1. 新建,编辑mybatis-config.xml文件(放入resources文件夹)
<?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/powernode"/>
                <property name="username" value="xxxx"/>
                <property name="password" value="xxxx"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="CarMapper.xml"/>
    </mappers>
</configuration>
  1. 新建,配置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">
<mapper namespace="org.mybatis.example.BlogMapper">
    <insert id="">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,1003,"面包车",13.00,"2020-10-13","飞行汽车")
    </insert>
</mapper>
  1. 在xxxMapper中编写sql代码(在3中已完成)

    ESP32-C3

  2. 把xxxMapper.txt文件路径放入mybatis-config.txt中(在2中已完成)

    自定义ava数据集

<mappers>
        <mapper resource="CarMapper.xml"/>
</mappers>

2.3 MyBatis中的事务

在mybatis-config.xml中有一行为

docker搭建gitlab

<transactionManager type="JDBC"/>

type类型可以写成两种,一种是JDBC另一种是MANAGED(不区分大小写)

react.js

  • JDBC:交给JDBC处理事务(默认false,表示开启事务,需要手动提交)
  • MANAGED:有用到spring框架时设置为此,表交给框架处理事务,如果没有用到框架设置为此类型,则没有人处理事务,会自动提交

注意事项:

web3.0

  • SqlSessionFactory.openSession()默认开启事务

2.4 编写MyBatis代码

   SqlSession sqlSession = null;
   try {
       SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
       SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsReader("mybatis-config.xml"));
       sqlSession = sqlSessionFactory.openSession();
       int count = sqlSession.insert("insertCar");
       System.out.println("新增了记录"+count);
       //提交事务
       sqlSession.commit();

   } catch (Exception e) {
       if (sqlSession != null) {
           sqlSession.rollback();
       }
       e.printStackTrace();
   }finally {
       if (sqlSession != null) {
           sqlSession.close();
       }
   }

2.5 在MyBatis中引入JUnit

在maven中添加junit依赖

adb 驱动获取

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

新建test包,以及创建CarMapperTest类
在这里插入图片描述
在新建的类中编写MyBatis代码

GradlePlugin


public class CarMapperTest {
    @Test
    public void insertCar(){
        SqlSession sqlSession = null;
        try {
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsReader("mybatis-config.xml"));
            sqlSession = sqlSessionFactory.openSession();
            int count = sqlSession.insert("insertCar");
            System.out.println("新增了记录"+count);
            //提交事务
            sqlSession.commit();

        } catch (Exception e) {
            if (sqlSession != null) {
                sqlSession.rollback();
            }
            e.printStackTrace();
        }finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }
}

运行,测试一下,绿色就是没问题
在这里插入图片描述
junit小知识点:

非阻塞赋值

  • 断言测试方法:Assert.assertEquals(期望值,实际值);

2.6 MyBatis集成日志组件

  • SLF4J沙拉疯):logback就是继承沙拉疯实现的
  • LOG4J
  • LOG4J2
  • STDOUT_LOGGING:MyBatis内部实现的日志组件,

logback、log4j、log4j2是同一个作者编写的

MySQL Workbench

若使用STDOUT_LOGGING,需要在mybatis-config.xml里添加配置文件
注意:settings标签必须添加在configuration下面

硬件工程

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

这边不使用STDOUT_LOGGING日志组件,我们使用最常用的logback组件
配置步骤:

DS18B20代码

  1. 在maven中添加logback的依赖
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
            <scope>test</scope>
        </dependency>
  1. 在mybatis-config.xml中删除日志设置(除了STDOUT_LOGGING自带日志组件外,其他组件不需要写日志设置)
<!-- 删除这些 -->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
  1. 写配置文件(名字必须为logback.xml或logback-test.xml)
    必须放在resources目录下
<?xml version="1.0" encoding="UTF-8"?>

<!-- 配置文件修改时重新加载,默认true -->
<configuration debug="false">

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" charset="UTF-8">
            <!-- 输出日志记录格式 -->
            <pattern>[%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    
<!--    mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>
    

    <!-- 日志输出级别,LOGBACK日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR-->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

  1. 运行测试

2.7 MyBatis工具类的编写

在编写代码中,每次都通过SqlSessionFactoryBuilder三步骤获取session太麻烦,我们编写一个工具类get会比较方便点

pinia

  1. 新建类utils.SqlSessionUtil
    在这里插入图片描述
  2. 编写代码
public class SqlSessionUtilTest {

    /**
     * 测试工具类
     */
    @Test
    public void openSessionTest(){
        SqlSession session = SqlSessionUtil.openSession();
        int count = session.insert("insertCar");
        System.out.println(count);
        session.commit();
        
        session.close();
    }
}
  1. 运行测试(数据库正常添加,完成)

三、使用MyBatis完成CRUD

3.1 实现Insert新增

1.1 MyBatis中完成Map集合传参

第一个入门程序已完成,现在我们来做MyBatis的CRUD操作
入门程序有个问题:实战的时候新增数据表行数据不可能是固定值
回顾:

DVE

<mapper namespace="org.mybatis.example.BlogMapper">
    <insert id="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,1003,"面包车",13.00,"2020-10-13","飞行汽车")
    </insert>
</mapper>
  1. 继续完善这个程序,新建一个模块叫CRUD,如图,把模块1的一些文件拷贝过来,maven依赖引用
    在这里插入图片描述
  2. 修改CarMapper.xml文件,加上占位符
    在MyBatis中占位符为#{},对应JDBC的?
    <insert id="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,#{},#{},#{},#{},#{})
    </insert>
  1. 编写测试代码
public class CarMapperTest {
    @Test
    public void insertCar(){
        SqlSession session = SqlSessionUtil.openSession();
        Map<String,Object> map = new HashMap<>();
        map.put("carNum","1004");
        map.put("brand","比亚迪汉");
        map.put("guidePrice",160.00);
        map.put("produceTime","2022-06-08");
        map.put("carType","新能源汽车");

        int count= session.insert("insertCar", map);
        System.out.println("新增的条目:"+count);
        session.commit();

        session.close();
    }
}
  1. 修改mapper文件中的占位符#{}(大括号中间填入map.get(key)里面的key)
<mapper namespace="org.mybatis.example.BlogMapper">
    <insert id="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>
</mapper>
  1. 测试,完成

1.2 MyBatis中通过POJO类完成传参

  1. 新建POJO包、类(pojo.Car)

public class Car {
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;
    //此处忽略构造方法、getting setting方法
    .....
}
  1. 编写测试代码
    @Test
    public void insertCar(){
        SqlSession session = SqlSessionUtil.openSession();
        
        Car car = new Car(null,"1005","比亚迪秦",30.0,"2020-10-20","新能源");
        
        int count= session.insert("insertCar", car);
        System.out.println("新增的条目:"+count);
        session.commit();

        session.close();
    }
  1. 编辑mapper文件
<mapper namespace="org.mybatis.example.BlogMapper">
    <insert id="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>
</mapper>
  1. 运行测试方法,总结:
    mapper文件中填写的#{carType}),MyBatis会通过反射机制查找getCarType()方法得到值

3.3 实现Delete删除

  1. 新增Mapper删除
    <delete id="deleteById">
        delete from t_car where id = #{id}
    </delete>
  1. 编写测试方法
    @Test
    public void deleteById(){
        SqlSession session = SqlSessionUtil.openSession();
        session.delete("deleteById",17);
        session.commit();
        session.close();
    }
  1. 注意事项
    当参数只有一个时,参数名可以随意,但是尽量做到见名知意

3.4 实现update修改

  1. 编写mapper
    <update id="updateById">
        update t_car 
          set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType}
        where id=#{id}
    </update>
  1. 编写测试类
    @Test
    public void testUpdateById(){
        SqlSession session = SqlSessionUtil.openSession();
        Car car = new Car(3L,"5123","哈哈车",1.5,"2011-01-04","新技术");
        int count = session.update("updateById", car);
        System.out.println(count);
        session.commit();
        session.close();
    }

3.5 实现select查询

1.1 selectOne查询一个

  1. 编写Mapper文件
    <select id="selectOneById" resultType="com.powernode.mybatis.pojo.Car">
        select car_num as carNum,
               brand,guide_price as guidePrice,
               guide_price as guidePrice,
               produce_time as produceTime,
               car_type as carType
        from t_car
        where id = #{id}
    </select>
  1. 编写测试方法
    @Test
    public void selectOneById(){
        SqlSession session = SqlSessionUtil.openSession();
        Car car = session.selectOne("selectOneById",16L);
        System.out.println(car);
        session.close();
    }

注意事项:

安装truffle框架

  • Mapper配置文件里要增加:resultType=“com.powernode.mybatis.pojo.Car”,指定查询出来是什么类,MyBatis会自动转换成该类
  • guide_price as guidePrice,取别名是为了让pojo类属性查询结果对应上符合ORM

1.2 selectList查询所有,返回一个集合

  1. 编写Mapper配置文件
    <select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
        select car_num as carNum,
               brand,guide_price as guidePrice,
               guide_price as guidePrice,
               produce_time as produceTime,
               car_type as carType
        from t_car
    </select>
  1. 编写测试代码
    @Test
    public void selectAll(){
        SqlSession session = SqlSessionUtil.openSession();
        List<Car> cars = session.selectList("selectAll");
        cars.forEach( car -> System.out.println(car));
        session.close();
    }

3.6 Mapper映射文件的namespace

在mapper.xml文件中有一个namespasce,这个属性是用来指定命名空间的,用来防止id重复
在java程序中的写法:

高斯数据库

        List<Car> cars = session.selectList("namespace.selectAll");

这样写才是严谨、完整的

swing

四、MyBatis核心配置文件详解

4.1 environment标签

一个environment对应一个SqlSessionFactory
一个SqlSessionFactory对应一个数据库

自定义异常

  1. 多环境下,配置文件这样写(两个environment)
    <environments default="development">
        <!-- 一个environment对应一个数据库 -->
        <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/powernode"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>

        <environment id="development2">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/powernode2"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
  1. 写测试方法
public class testConfiguration {
    @Test
    public void testEnvironment() throws IOException {
        SqlSessionFactoryBuilder sessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sessionFactory = sessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "development2");
        SqlSession session = sessionFactory.openSession();
        int count = session.insert("insertCar");
        System.out.println(count);
        session.commit();
        session.close();
    }
}

4.2 transactionManager标签

在mybatis-config.xml配置文件中,有transactionManager子标签,表示设置MyBatis的事务管理器

简单工厂模式

<transactionManager type="JDBC"/>

MyBatis中有两个事务管理器

边缘计算

  • JDBC:交给原生JDBC管理setAutoComit(false);需要手动comit()提交
  • MANAGED:交给JEE处理,一般由JEE框架处理,如Spring

MyBatis底层有一个Transaction接口,实现两个事务

eosio

  • 如果type=“JDBC”,那么底层会实例化JdbcTransaciton对象
  • 如果type=“MANAGED”,那么底层会实例化ManagedTransaciton

4.3 dataSource标签

<dataSource type="POOLED">
	.......
</dataSource>

问:这个标签有啥用
答:dataSource表示数据源,用来获取Connection对象

它的type属性可以填写三个值:

  • UNPOOLED:不使用数据库连接池,每次获取Connection都要创建一个新对象
  • POOLED:使用MyBatis自带的连接池
  • JNDI:连接第三方的数据源(如果自己手写框架也可以用这个)

3.1 type属性详解

不同类型的数据源下有不同的属性,比如

<dataSource type="POOLED">
	....
	<properties/>
	....
</dataSource>

<dataSource type="JNDI">
	....
	<properties/>
	....
</dataSource>

<properties/>

数量、内容是不一样的,具体需要看官方文档
JDNI是:java命名目录接口,Tomcat服务器实现了这个规范
假如需要用第三方数据源,Tomcat中实现了这个数据源,则在properties中填写对应的数据,要和tomat对应上

3.2 pooled和unpooled的区别

  1. unpooled表示不使用连接池,每次请求过来都会创建一个Connection
  2. pooled表示使用MyBatis自带的连接池:请求过来会先从连接池获取Connection对象

:使用连接池有什么好处?

3. 迟内的Connection数量是固定的,比如池子大小是5,如果5个连接都被占用,第6个要获取连接就先等待,数量固定
4. 假如有人一直F5刷新,没有用连接池的话,就会一直创建Connection对象,如果实例化对象过多,可能会导致服务器宕机,数量固定
5. 有新请求,第一反应去池中查找,可以增加效率

3.3 配置具体的数据库连接池参数

<!--连接池最大连接数,默认:10-->
<property name="poolMaximumActiveConnections " value="10"/>
<!--可以同时存在的最大空闲连接数,空闲太多则真正关闭一些Connection-->
<property name="poolMaximumIdleConnections " value="5"/>
<!--超时强制关闭时间,默认20000-->
<property name="poolMaximumCheckoutTime " value="20000"/>
<!--如果连接花费时间很长,连接池会隔断时间尝试重新连接并打印日志-->
<property name="poolTimeToWait " value="2000"/>

4.4 properties标签

1. 第一种写法(写在配置文件内)

在configuration标签下有一个properties子标签,是用来设置变量的

<property name="key" value="value"/>

这样写相当于往map集合里放入一个key,可以通过key拿到value,在mybatis-config.xml的其他地方,可以写成${key}获取到value

2. 第二种写法(外部文件-相对路径)

<properties resource="jdbc.properties"/>

然后在外部新建一个jdbc.properties,如(等号左边是key右边是value)

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/powernode
username=root
password=root

3. 第三种写法(外部文件-绝对路径)

不推荐这种写法

<properties url="file:///D://jdbc.properties"/>

五、在Web中应用MyBatis(使用MVC架构模式)

5.1 环境搭建

建立如下数据库:
在这里插入图片描述

5.2 idea环境搭建

1. idea新建一个maven项目,Create from archetype勾选上,选择如图所示webapp

在这里插入图片描述

2. maven依赖配置好

 <dependencies>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.30</version>
    </dependency>
    <dependency>
      <groupId>servletapi</groupId>
      <artifactId>servletapi</artifactId>
      <version>2.4-20040521</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.11</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.11</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

  </dependencies>

3. 修改web.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"
         metadata-complete="true">
</web-app>

4. 编写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>
    <properties resource="jdbc.properties"/>
    <environments default="development">
        <!-- 一个environment对应一个数据库 -->
        <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>

        <environment id="development2">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/powernode2"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="ActMapper.xml"/>
    </mappers>
</configuration>

5. 在resources目录下新建、编辑jdbc.properties文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/powernode
username=root
password=root

6. 在resources目录下新建、编写Mapper文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="account">
    <insert id="insertAct">
        insert into t_act(id,name,balance)
        values (null,#{name},#{balance})
    </insert>
</mapper>

7. 在resources目录下新建、编写logback.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>

<!-- 配置文件修改时重新加载,默认true -->
<configuration debug="false">

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" charset="UTF-8">
            <!-- 输出日志记录格式 -->
            <pattern>{%thread} %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

<!--    mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,LOGBACK日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR-->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

8. 新建pojo类,放到com.powernode.bank.pojo包中

public class Account {
    private Long id;
    private String name;
    private Double balance;
......
//此处省略构造方法、getting setting toString方法,请自行创建

9. 新建dao, servce, utils, web,并在utils中新建SqlSessionUtil类

在这里插入图片描述

package com.powpernode.bank.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;

/**
 * @author huochai
 * @date 2022/10/15 9:22
 */

public class SqlSessionUtil {

    /**
     * 一个sessionFactory对应一个environment(数据库),所以不要每次运行都new一个新的
     */

    /**
     * 构造方法设置为私有的,防止被实例化
     */
    private SqlSessionUtil(){}

    private static SqlSessionFactory sessionFactory;
    static {
        try {
            sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession openSession(){
        SqlSession session = sessionFactory.openSession();
        return session;
    }
}

10. 在webapp中新建index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>银行转账</title>
</head>
<body>
<form action="/bank/transfer" method="post">
  转出账户:<input type="text" name="fromActno"><br>
  转入账户:<input type="text" name="toActno"><br>
  转账金额<input type="text" name="money"><br>
  <input type="submit" value="转账">
</form>
</body>
</html>

5.3 后端代码实现

根据前端请求发送的路线完善后端代码

1. 新建、完善AccountServlet(接口与实现类)

package com.powpernode.bank.web;

import com.powpernode.bank.exception.MoneyNotEnoughException;
import com.powpernode.bank.exception.TransferNotSuccessException;
import com.powpernode.bank.service.AccountService;
import com.powpernode.bank.service.impl.AccountServiceImpl;

import javax.jws.WebService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author huochai
 * @date 2022/10/16 15:15
 */

@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
    /**
     * 调用业务类处理业务
     */
    private AccountService accountService = new AccountServiceImpl();
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //接收前端传来的参数
        String fromActno = request.getParameter("fromActno");
        String toActno = request.getParameter("toActno");
        Double money = Double.parseDouble(request.getParameter("money"));

        //调用业务类处理转账
        try {
            accountService.transfer(fromActno,toActno,money);
            response.sendRedirect(request.getContextPath()+"/success.html");
        } catch (MoneyNotEnoughException e) {
            e.printStackTrace();
            response.sendRedirect(request.getContextPath()+"/error1.html");
        } catch (TransferNotSuccessException e) {
            e.printStackTrace();
            response.sendRedirect(request.getContextPath()+"/error2.html");
        }

    }
}

2. 完善业务层AccountService接口、实现类

package com.powpernode.bank.service;

import com.powpernode.bank.exception.MoneyNotEnoughException;
import com.powpernode.bank.exception.TransferNotSuccessException;

/**
 * @author huochai
 * @date 2022/10/16 15:17
 */
public interface AccountService {
    /**
     * 转账业务方法
     * @param fromAct 转出账户
     * @param toAct 转入账户
     * @param money 转账金额
     * @throws MoneyNotEnoughException 转出账户余额不足异常
     * @throws TransferNotSuccessException 转账失败异常
     */
    void transfer(String fromAct,String toAct,Double money) throws MoneyNotEnoughException, TransferNotSuccessException;
}

package com.powpernode.bank.service.impl;
import com.powpernode.bank.dao.AccountDao;
import com.powpernode.bank.dao.impl.AccountDaoImpl;
import com.powpernode.bank.exception.MoneyNotEnoughException;
import com.powpernode.bank.exception.TransferNotSuccessException;
import com.powpernode.bank.pojo.Account;
import com.powpernode.bank.service.AccountService;
import javax.jws.WebService;
/**
 * @author huochai
 * @date 2022/10/16 15:19
 * 只负责处理业务逻辑,不涉及数据库修改
 */

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromAct, String toAct, Double money) throws MoneyNotEnoughException, TransferNotSuccessException {
        //判断余额是否充足
        Account fromAccount = accountDao.selectById(Long.parseLong(fromAct));
        if (fromAccount.getBalance()<money) {
            throw new MoneyNotEnoughException("对不起,余额不足");
        }

        //将fromAct减少money,toAct增加money
        Account toAccount = accountDao.selectById(Long.parseLong(toAct));
        fromAccount.setBalance(fromAccount.getBalance()-money);
        toAccount.setBalance(toAccount.getBalance()+money);
        //更新两个账户
        int count = accountDao.updateAccount(fromAccount);
        count += accountDao.updateAccount(toAccount);
        
        if (count<2){
            throw new TransferNotSuccessException("转账失败,未知错误");
        }
    }
}

3. 完善DAO层,接口、实现类

package com.powpernode.bank.dao;
import com.powpernode.bank.pojo.Account;
/**
 * @author huochai
 * @date 2022/10/16 15:20
 */
public interface AccountDao {
    /**
     * 根据ID查询账户
     * @param id id
     * @return 返回账户
     */
    Account selectById(Long id);

    /**
     * 更新用户
     * @param account 需要更新的用户
     * @return 1表示更新完成
     */
    int updateAccount(Account account);

    /**
     * 插入用户
     * @param account 需要插入的用户
     * @return 1表示插入完成
     */
    int insertAccount(Account account);
}

package com.powpernode.bank.dao.impl;
import com.powpernode.bank.dao.AccountDao;
import com.powpernode.bank.pojo.Account;
import com.powpernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
/**
 * @author huochai
 * @date 2022/10/16 15:21
 */
public class AccountDaoImpl implements AccountDao {
    @Override
    public Account selectById(Long id) {
        SqlSession session = SqlSessionUtil.openSession();
        Account account = session.selectOne("account.selectById", id);
        session.close();
        return account;
    }

    @Override
    public int updateAccount(Account account) {
        SqlSession session = SqlSessionUtil.openSession();
        int count = session.update("account.updateById", account);
        session.commit();
        return count;
    }

    @Override
    public int insertAccount(Account account) {
        SqlSession session = SqlSessionUtil.openSession();
        int count = session.insert("account.insertAct", account);
        session.commit();
        return count;
    }
}

4. 完善两个异常类

/**
 * @author huochai
 * @date 2022/10/16 15:47
 */
public class MoneyNotEnoughException extends Exception{
    public MoneyNotEnoughException(){};
    public MoneyNotEnoughException(String msg){
        super(msg);
    }
}
package com.powpernode.bank.exception;

/**
 * @author huochai
 * @date 2022/10/16 15:51
 */
public class TransferNotSuccessException extends Exception{
    public TransferNotSuccessException(){};
    public TransferNotSuccessException(String msg){
        super(msg);
    }
}

5. 当前项目结构

在这里插入图片描述

6. 完善事务处理

目前为止项目里存在一个问题,没有事务处理机制
如果在更新完账户1之后异常,就会出现少钱的现象
在这里插入图片描述

6.1 加入线程池

/**
* 完善后的SqlSessionUtil工具类
*/
package com.powpernode.bank.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;

/**
 * @author huochai
 * @date 2022/10/15 9:22
 */

public class SqlSessionUtil {

    public static ThreadLocal<SqlSession> local = new ThreadLocal();
    private SqlSessionUtil(){}
    private static SqlSessionFactory sessionFactory;
    static {
        try {
            sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession openSession(){
        SqlSession session = local.get();
        if (session==null) {
            session = sessionFactory.openSession();
            local.set(session);
        }
        return session;
    }

    public static void close(SqlSession session){
        if (session!=null) {
            session.close();
            local.remove();
        }
    }
}

6.2 在业务层控制事务处理

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromAct, String toAct, Double money) throws MoneyNotEnoughException, TransferNotSuccessException {

        SqlSession session = SqlSessionUtil.openSession();
        //判断余额是否充足
        Account fromAccount = accountDao.selectById(Long.parseLong(fromAct));
        if (fromAccount.getBalance()<money) {
            throw new MoneyNotEnoughException("对不起,余额不足");
        }


        //将fromAct减少money,toAct增加money
        Account toAccount = accountDao.selectById(Long.parseLong(toAct));
        fromAccount.setBalance(fromAccount.getBalance()-money);
        toAccount.setBalance(toAccount.getBalance()+money);

        //更新两个账户
        int count = accountDao.updateAccount(fromAccount);
        count += accountDao.updateAccount(toAccount);

        if (count<2){
            throw new TransferNotSuccessException("转账失败,未知错误");
        }

        session.commit();
        SqlSessionUtil.close(session);
    }
}

5.4 MyBatis作用域(Scope)和生命周期

名称 生命周期
SqlSessionFactoryBuilder 只是用来buildFactory的,利用完即可丢弃
SqlSessionFactory 一个数据库对应一个Factory,最好不要丢弃
SqlSession 一个线程对应一个SqlSession

观察项目还有什么问题,发现DAO层代码很少并且很固定

public class AccountDaoImpl implements AccountDao {
//可以发现第一行都是获取Session,第二行执行,第三行return
    @Override
    public Account selectById(Long id) {
        SqlSession session = SqlSessionUtil.openSession();
        Account account = session.selectOne("account.selectById", id);
        return account;
    }

    @Override
    public int updateAccount(Account account) {
        SqlSession session = SqlSessionUtil.openSession();
        int count = session.update("account.updateById", account);
        return count;
    }

    @Override
    public int insertAccount(Account account) {
        SqlSession session = SqlSessionUtil.openSession();
        int count = session.insert("account.insertAct", account);
        return count;
    }
}

有没有什么框架可以代替这种简单重复的工作呢?
答:可以利用javassist框架
通过使用Javassist对字节码操作为JBoss实现动态“AOP”框架

5.5 使用javassist动态生成类

public class JavassistTest{
    @Test
    public void generateClass() throws Exception {
        //获取类池
        ClassPool classPool = ClassPool.getDefault();
        // 制造类
        CtClass ctClass = classPool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
        // 制造方法
        String method = "public void insert(){System.out.println(166);}";
        CtMethod make = CtMethod.make(method, ctClass);
        // 把方法加到类中
        ctClass.addMethod(make);

        //在内存中生成类
        ctClass.toClass();

        Class<?> aClass = Class.forName("com.powernode.bank.dao.impl.AccountDaoImpl");
        Object o = aClass.newInstance();
        Method insert = aClass.getDeclaredMethod("insert");
        
        insert.invoke(o);
    }
}

5.6 使用javassist生成类并实现接口

    @Test
    public void generateInterface() throws Exception{
        //获取类池
        ClassPool pool = ClassPool.getDefault();
        //制造类
        CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
        //制造接口
        CtClass ctClass1 = pool.makeInterface("com.powernode.javassist.test.AccountDao");
        //添加接口
        ctClass.addInterface(ctClass1);
        CtMethod make = CtMethod.make("public void delete(){System.out.println(\"Hello delete\");}", ctClass);
        ctClass.addMethod(make);
        //装载类
        Class<?> aClass = ctClass.toClass();
        AccountDao o = (AccountDao) aClass.newInstance();
        o.delete();
    }

5.7 使用javassist动态实现接口中所有方法

	@Test
    public void generateInterfaceAll() throws Exception{
        //获取类池
        ClassPool pool = ClassPool.getDefault();
        //制造类
        CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
        //制造接口
        CtClass ctInterface = pool.makeInterface("com.powernode.javassist.test.AccountDao");
        //类实现接口
        ctClass.addInterface(ctInterface);

        Method[] declaredMethods = AccountDao.class.getDeclaredMethods();
        //制造方法
        //方法内容

        Arrays.stream(declaredMethods).forEach(method -> {
            try {
                StringBuffer methodValue = new StringBuffer();
                methodValue.append("public ");
                methodValue.append(method.getReturnType().getName()+" ");
                methodValue.append(method.getName()+"(");
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    methodValue.append(parameterTypes[i].getName()+" ");
                    methodValue.append("args"+i);
                    if (i!= parameterTypes.length-1){
                        methodValue.append(",");
                    }
                }
                methodValue.append(")");
                methodValue.append("{System.out.println(\"hello all\");");
                if ("void".equals(method.getReturnType().getName())){
                }else if ("int".equals(method.getReturnType().getName())){
                    methodValue.append("return 1;");
                }else if("java.lang.String".equals(method.getReturnType().getName())){
                    methodValue.append("return \"666\";");
                }
                methodValue.append("}");

                CtMethod make = CtMethod.make(String.valueOf(methodValue), ctClass);
                ctClass.addMethod(make);

            } catch (CannotCompileException e) {
                e.printStackTrace();
            }
        });

        Class<?> toClass = ctClass.toClass();

        AccountDao accountDao = (AccountDao) toClass.newInstance();
        accountDao.delete();
        accountDao.insert("666",50.00);
        accountDao.selectByActno("555");
    }

5.8 编写工具类GenerateDaoProxy(自动实现DAO层接口)

注意:若想利用GenerateDaoProxy,
则Mapper.xml文件的namespace必须为DAO层接口的全类名
sqlId必须为DAO层接口中的方法

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

<mapper namespace="com.powpernode.bank.dao.AccountDao">
    <insert id="insertAccount">
        insert into t_act(id,name,balance)
        values (null,#{name},#{balance})
    </insert>

    <select id="selectById" resultType="com.powpernode.bank.pojo.Account">
        select * from t_act where id=#{id}
    </select>

    <update id="updateAccount">
        update t_act set name=#{name},balance=#{balance} where id=#{id}
    </update>
</mapper>
package com.powpernode.bank.utils;

import org.apache.ibatis.javassist.CannotCompileException;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author huochai
 * @date 2022/10/16 23:30
 */
public class GenerateDaoProxy {


    /**
     * 这个工具类是框架的开发者提供的
     * 开发者可以给使用者规定传进哪些参数
     *
     * 传进接口,返回实现所有方法的类
     * @param daoInterface 接口
     * @return Impl类
     */
    public static Object generate(SqlSession session, Class daoInterface)  {
        //类池
        ClassPool pool = ClassPool.getDefault();
        //制造类
        CtClass ctClass = pool.makeClass(daoInterface.getName() + "Proxy");
        //制造接口
        CtClass ctInterface = pool.makeInterface(daoInterface.getName());
        ctClass.addInterface(ctInterface);

        Method[] declaredMethods = daoInterface.getDeclaredMethods();

        Arrays.stream(declaredMethods).forEach(method -> {
            try {
                StringBuffer methodCode = new StringBuffer();
                //添加修饰符
                methodCode.append("public ");
                //添加返回值
                methodCode.append(method.getReturnType().getName()+" ");
                methodCode.append(method.getName());
                methodCode.append("(");

                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    methodCode.append(parameterTypes[i].getName()+" ");
                    methodCode.append("arg"+i);
                    if (i!= parameterTypes.length-1){
                        methodCode.append(",");
                    }
                }
                methodCode.append("){");
                /**
                 * 括号中间需要写对应的session.insert或session.select方法
                 */
                String sqlId = daoInterface.getName()+"."+method.getName();
                SqlCommandType sqlCommandType = session.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
                methodCode.append("org.apache.ibatis.session.SqlSession session = com.powpernode.bank.utils.SqlSessionUtil.openSession();");

                if(sqlCommandType == SqlCommandType.INSERT){

                }
                if(sqlCommandType == SqlCommandType.DELETE){

                }
                if(sqlCommandType == SqlCommandType.UPDATE){
                    methodCode.append("return session.update(\""+sqlId+"\", arg0);");
                }
                if(sqlCommandType == SqlCommandType.SELECT){
                    String resultType = method.getReturnType().getName();
                    methodCode.append("return ("+resultType+")session.selectOne(\""+sqlId+"\", arg0);");
                }

                methodCode.append("}");

                System.out.println(methodCode.toString());
                CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
                ctClass.addMethod(ctMethod);
            } catch (CannotCompileException e) {
                e.printStackTrace();
            }
        });

        Object obj = null;
        try {
            Class<?> toClass = ctClass.toClass();
            obj = toClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return obj;
    }
}

最后在业务层引用Dao的时候改一下即可:

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(SqlSessionUtil.openSession(),AccountDao.class);
.....

写完以后得知一个好消息,MyBatis已经实现了映射机制,不用自己手写代码了(要求和上面一样,对namespace以及sqlId有格式要求)

//自己写的
private AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(SqlSessionUtil.openSession(),AccountDao.class);
//MyBatis自带
private AccountDao accountDao = SqlSessionUtil.openSession().getMapper(AccountDao.class);

5.9 面向接口的方式进行CRUD

新建maven模块mybatis-005-crud2
添加maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>MyBatis</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>


        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <groupId>com.powernode</groupId>
    <artifactId>mybatis-005-crud2</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

添加mapper类

/**
 * @author huochai
 * @date 2022/10/17 9:38
 * 在MyBatis中,一般不叫XXXDao,一般叫xxxMapper
 */
public interface CarMapper {
    /**
     * 插入数据
     * @param car
     * @return 返回1表示正常
     */
    int insert(Car car);

    /**
     * 根据ID删除
     * @param id
     * @return 返回1表示正常
     */
    int deleteById(Long id);

    /**
     * 更新数据
     * @param car
     * @return 返回1表示正常
     */
    int update(Car car);

    /**
     * 根据ID查询
     * @param id
     * @return 返回1表示正常
     */
    Car selectById(Long id);

    /**
     * 查询所有的数据
     * @return 返回给List集合
     */
    List<Car> selectAll();
}

编写CarMapper.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">
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
    <insert id="insert">
      insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
      values(null, #{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>

    <delete id="delete">
      delete from t_car where id=#{id}
    </delete>


    <update id="update">
      update t_car
      set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType}
      where id=#{id}
    </update>


    <select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
      select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
      from t_car where id=#{id}
    </select>

    <select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
        select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from t_car
    </select>
</mapper>

新建工具类,SqlSessionUtil

public class SqlSessionUtil {
    
    public static ThreadLocal<SqlSession> local = new ThreadLocal();
    private SqlSessionUtil(){}
    private static SqlSessionFactory sessionFactory;
    static {
        try {
            sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession openSession(){
        SqlSession session = local.get();
        if (session==null) {
            session = sessionFactory.openSession();
            local.set(session);
        }
        return session;
    }

    public static void close(SqlSession session){
        if (session!=null) {
            session.close();
            local.remove();
        }
    }
}

编写测试用例

public class TestMapper {
    @Test
    public void insert(){
        SqlSession session = SqlSessionUtil.openSession();
        CarMapper mapper = session.getMapper(CarMapper.class);
        Car car = new Car(null,"5556","兰博基尼六",1952.1,"2060-02-06","跑车");
        mapper.insert(car);
        session.commit();
    }
    @Test
    public void delete(){
        SqlSession session = SqlSessionUtil.openSession();
        CarMapper mapper = session.getMapper(CarMapper.class);
        mapper.deleteById(21L);
        session.commit();
    }
    @Test
    public void update(){
        SqlSession session = SqlSessionUtil.openSession();
        CarMapper mapper = session.getMapper(CarMapper.class);
        Car car = new Car(18L,"5556","兰博基尼六",1952.1,"2060-02-06","跑车");
        mapper.update(car);
        session.commit();
    }
    @Test
    public void selectById(){
        SqlSession session = SqlSessionUtil.openSession();
        CarMapper mapper = session.getMapper(CarMapper.class);
        Car car = mapper.selectById(18L);
        System.out.println(car);
    }
    @Test
    public void selectAll(){
        SqlSession session = SqlSessionUtil.openSession();
        CarMapper mapper = session.getMapper(CarMapper.class);
        List<Car> cars = mapper.selectAll();
        cars.forEach(car -> System.out.println(car));
    }
}

六、MyBatis小技巧

6.1 #{}和${}的区别

  • #{property}:底层使用PreparedStatement。特点:先进行SQL语句的编译,然后给SQL语句的占位符?传值。可以避免SQL注入的风险
  • ${property}:底层使用Statement。特点:先进行SQL语句的拼接,然后再对SQL语句进行编译。存在SQL注入的风险

6.2 什么时候使用${}

  1. 如果需要SQL语句的关键字放到SQL语句中,只能使用${},因为#{}是以值的形式放到SQL语句当中的。
    例:
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
    <select id="selectByType" resultType="com.powernode.mybatis.pojo.Car">
        select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from t_car
        order by produce_time ${ascOrDesc}
    </select>
</mapper>
public class CarMapperTest {
    @Test
    public void selectAllSortedTest(){
        SqlSession session = SqlSessionUtil.openSession();
        List<Car> cars = session.getMapper(CarMapper.class).selectByType("desc");
        cars.forEach( car -> System.out.println(car));
        session.close();
    }
}
  1. 拼接表名的时候,比如需要记录日志信息,如果每天都往同一个日志表中存储数据,慢慢的表信息就会越来越多,可以采用加日期分类的方法,直接查询天表可以增加效率,如t_log_20221017这样分类(然后拼接表名就可以采用${}的方法)
  2. 批量删除,可以写,删除用户123、456、8/7
delete from t_car where id in (123,456,789)
//用美元括号
delete from t_car where id in (${})

6.3 查找包含某个关键词str的方法

  1. concat()函数
    <select id="selectLikeSome" resultType="com.powernode.mybatis.pojo.Car">
        select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from t_car
        where car_type like Concat('%',#{str},'%')
    </select>
  1. 用双引号把通配符引出去,让#{}在外面好被jdbc检测到(常用)
    <select id="selectLikeSome" resultType="com.powernode.mybatis.pojo.Car">
        select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
        from t_car
        where car_type like "%"#{str}"%"
    </select>

6.4 MyBatis中起别名

namespace不能起别名
所有别名不区分大小写

    <typeAliases>
        <!--给type起别名,可以直接用alias名称读取到-->
        <typeAlias type="com.powernode.mybatis.pojo.Car" alias="car"/>
        <!--省略alias,默认就是类简名,比如car-->
        <typeAlias type="com.powernode.mybatis.pojo.Car"/>
        <!--包下所有类自动起别名,不区分大小写-->
        <package name="com.powernode.mybatis.pojo"/>
    </typeAliases>

6.5 MyBatis小技巧之Mapper的配置

    <mappers>
        <!--1. 根路径下查找CarMapper.xml文件-->
        <mapper resource="CarMapper.xml"/>
        <!--2. 绝对路径查找-->
        <mapper url="file://c:/CarMapper.xml"/>
        <!--3. 查找映射接口同级目录下的Mapper.xml文件-->
        <!--   com/powernode/mybatis/mapper 建包需要这样建-->
        <mapper class="com.powernode.mybatis.mapper.CarMapper"/>
        <!--最常用:路径下自动查找接口对应名字xml文件-->
        <package name="com.powernode.mybatis.mapper"/>
    </mappers>

6.6 插入数据时获取自动生成的主键

插入一条数据的时候,自动返回主键到制定属性中

useGeneratedKeys=“true” 表示使用自动生成的主键值
keyProperty=“id” 制定属性值赋值给对象的哪个属性

    <insert id="insertCar" useGeneratedKeys="true" keyProperty="id">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values (null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>
    @Test
    public void insertCar(){
        SqlSession session = SqlSessionUtil.openSession();
        Car car = new Car(null,"9851","比亚迪ao",30.0,"2020-10-20","新能源");
        session.getMapper(CarMapper.class).insertCar(car);
        System.out.println(car);
        session.close();
    }

结果:Car{id=22, carNum=‘9851’, brand=‘比亚迪ao’, guidePrice=30.0, produceTime=‘2020-10-20’, carType=‘新能源’}
把自动递增的主键返回给了id

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注