自己学习Mybatis做的记录
目录
[1.1 框架](#1.1 框架)
[1.2 三层架构](#1.2 三层架构)
[1.3 JDBC的不足](#1.3 JDBC的不足)
[1.4 了解MyBatis](#1.4 了解MyBatis)
[2.0 MyBatis架构](#2.0 MyBatis架构)
[2.0.1 整体架构](#2.0.1 整体架构)
[2.0.2 原理分析](#2.0.2 原理分析)
[2.1 版本](#2.1 版本)
[2.2 MyBatis下载](#2.2 MyBatis下载)
[2.3 MyBatis入门程序开发步骤](#2.3 MyBatis入门程序开发步骤)
[2.3.1 第一个MyBatis程序的细节](#2.3.1 第一个MyBatis程序的细节)
[2.3.2 MyBatis的事务管理机制](#2.3.2 MyBatis的事务管理机制)
[2.5 MyBatis第一个比较完整的代码写法](#2.5 MyBatis第一个比较完整的代码写法)
[2.7 引入日志框架logback](#2.7 引入日志框架logback)
[2.8 MyBatis工具类SqlSessionUtil的封装](#2.8 MyBatis工具类SqlSessionUtil的封装)
[2.9 映射文件中Mapper标签的namespace属性](#2.9 映射文件中Mapper标签的namespace属性)
[4.1 environment](#4.1 environment)
[4.2 transactionManager](#4.2 transactionManager)
[4.3 dataSource](#4.3 dataSource)
[4.4 .properties属性文件](#4.4 .properties属性文件)
[4.5 mappers标签](#4.5 mappers标签)
[3.1 insert(Create)](#3.1 insert(Create))
[3.2 delete(Delete)](#3.2 delete(Delete))
[3.3 update(Update)](#3.3 update(Update))
[3.4 select(Retrieve)](#3.4 select(Retrieve))
[3.5 总结](#3.5 总结)
[8.1 Mybatis接口代理机制的理解](#8.1 Mybatis接口代理机制的理解)
[8.2 接口代理机制的开发规范](#8.2 接口代理机制的开发规范)
[8.3 接口代理机制实例(005-crud2)](#8.3 接口代理机制实例(005-crud2))
[十一、#{}和{}的区别](#{}和{}的区别)
[使用{}](#使用{})
[什么情况下必须使用{}](#什么情况下必须使用{})
在前面我们学习MySQL数据库时,都是利用图形化客户端工具(如:idea、navicat),来操作数据库,但我们做为后端程序开发人员,通常会使用Java程序来完成对数据库的操作。Java程序操作数据库,现在主流的方式是:Mybatis。
MyBatis是一款优秀的 持久层 框架,用于简化JDBC的开发。
一、MyBatis概述
1.1 框架
-
在文献中看到的framework被翻译为框架
-
Java常用框架:
-
SSM三大框架:Spring + SpringMVC + MyBatis
-
SpringBoot
-
SpringCloud
-
等。。
-
-
框架其实就是对通用代码的封装,提前写好的一堆接口和类, 我们可以在做项目的时候直接引入框架,基于这些现有的接口和类进行开发,可以大大提高开发效率。
-
框架一般都以jar包的形式存在。(jar包中有class文件以及各种配置文件等。)
-
SSM三大框架的学习顺序:
- 方式一:MyBatis、Spring、SpringMVC(建议)
1.2 三层架构
-
表现层(UI):直接跟前端打交互(一是接收前端ajax请求,二是返回json数据给前端)
-
业务逻辑层(BLL):一是处理 表现层转发过来的前端请求 (也就是具体业务),二是将从持久层获 取的数据 返回到表现层。
-
数据访问层(DAL):操作数据库完成数据库表的CRUD,并将获得的数据返回到业务逻辑层 层。
-
Java持久层的框架:
-
MyBatis(其实就是对JDBC进行了封装)
-
Hibernate(实现了JPA规范)
-
jOOQ
-
Guzz
-
Spring Data(实现了JPA规范)
-
ActiveJDBC
-
......
-
1.3 JDBC的不足
- 示例代码1:
java
// ......
// sql语句写死在java程序中,将来可能有需求将会修改这些java程序,违背OCP原则
String sql = "insert into t_user(id,idCard,username,password,birth,gender,
email,city,street,zipcode,phone,grade) values(?,?,?,?,?,?,?,?,?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
// JDBC代码繁琐的赋值:思考一下,这种有规律的代码能不能通过反射机制来做自动化。
ps.setString(1, "1");
ps.setString(2, "123456789");
ps.setString(3, "zhangsan");
ps.setString(4, "123456");
ps.setString(5, "1980-10-11");
ps.setString(6, "男");
ps.setString(7, "zhangsan@126.com");
ps.setString(8, "北京");
ps.setString(9, "大兴区凉水河二街");
ps.setString(10, "1000000");
ps.setString(11, "16398574152");
ps.setString(12, "A");
// 执行SQL
int count = ps.executeUpdate();
// ......
示例代码2:
java
// ......
// sql语句写死在java程序中
String sql = "select id,idCard,username,password,birth,gender,email,city,s
treet,zipcode,phone,grade from t_user";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
List<User> userList = new ArrayList<>();
// 思考以下循环中的所有代码是否可以使用反射进行自动化封装。
while(rs.next()){
// 获取数据
String id = rs.getString("id");
String idCard = rs.getString("idCard");
String username = rs.getString("username");
String password = rs.getString("password");
String birth = rs.getString("birth");
String gender = rs.getString("gender");
String email = rs.getString("email");
String city = rs.getString("city");
String street = rs.getString("street");
String zipcode = rs.getString("zipcode");
String phone = rs.getString("phone");
String grade = rs.getString("grade");
// 创建对象
User user = new User();
// 给对象属性赋值
user.setId(id);
user.setIdCard(idCard);
user.setUsername(username);
user.setPassword(password);
user.setBirth(birth);
user.setGender(gender);
user.setEmail(email);
user.setCity(city);
user.setStreet(street);
user.setZipcode(zipcode);
user.setPhone(phone);
user.setGrade(grade);
// 添加到集合
userList.add(user);
}
// ......
-
JDBC不足:
-
SQL语句写死在Java程序中,不灵活,改SQL的话就要改Java代码,违背开闭原则OCP。
-
给?传值繁琐。
-
将查询结果集封装成Java对象是繁琐的。
-
1.4 了解MyBatis
-
MyBatis本质上是对JDBC的封装,使开发者只需要关注 SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等JDBC繁杂的过程代码,通过MyBatis完成数据库表的CRUD。
-
MyBatis通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 Java 对象和statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由MyBatis框架执行sql并将结果映射成Java对象并返回。(在Mybati中的statement是要执行的SQL)
-
MyBatis在三层架构中负责持久层的,属于持久层框架。
-
MyBatis的发展历程:【引用百度百科】
-
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁 移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
-
iBATIS一词来源于"internet"和"abatis"的组合,是一个基于Java的持久层框架。iBATIS提供的 持久层框架包括SQL Maps和Data Access Objects(DAOs)。
-
-
打开mybatis代码可以看到它的包结构中包含:ibatis
-
ORM:对象关系映射(其实是一种思想)
-
O(Object):Java对象
-
R(Relational):关系型数据库
-
M(Mapping):Java对象映射成(转换成) 数据库表中的一行记录**,或是将** 数据库表中一行记录映射成Java虚拟机中的一个Java对象。**
-
ORM图示:
-
-
-
MyBatis属于半自动化的ORM框架。
-
Hibernate属于全自动化的ORM框架。
-
-
MyBatis框架特点:
-
支持定制化的SQL、存储过程、基本映射以及高级映射
-
几乎避免了所有JDBC中要手动设置参数以及获取结果集的代码
-
支持XML开发,也支持注解式开发。【为了保证sql语句的灵活,所以mybatis大部分是采用 XML方式开发。】
-
将接口和 Java 的 POJOs(Plain Ordinary Java Object,简单普通的Java对象)映射成数据库中的 记录
-
体积小好学:两个jar包,两个XML配置文件。
-
完全做到sql解耦合。
-
提供了基本映射标签。
-
提供了高级映射标签。
-
提供了XML标签,支持动态SQL的编写。
-
.....
-
二、MyBatis入门
2.0 MyBatis架构
2.0.1 整体架构
1、MyBatis配置:
-
mybatis-config.xml,此文件作为MyBatis的全局(核心)配置文件,配置了MyBatis的运行环境等信息。
-
mapper.xml文件即SQL映射文件,该文件中配置了操作数据库的sql语句,此文件需要在mybatis-config.xml中加载。
2、通过MyBatis环境等配置信息构造SqlSessionFactory,即会话工厂。
3、由会话工厂创建SqlSession即会话,SqlSession对象中有很多操作数据库CRUD的方法,SqlSession是对Connection对象的封装。
4、MyBatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5、MappedStatement也是MyBatis一个底层封装对象,Mybatis将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。在mapper.xml文件中一个标签+sql语句就是一个MappedStatement对象,sql语句用到的参数将通过Executor执行器传过来,sql的id即是MappedStatement的id。
6、MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、字符串类型、实体类类型,Executor在MappedStatement执行sql前将输入的Java对象映射至sql中,输入参数映射就是 JDBC编程中对PreparedStatement设置参数。
7、MappedStatement对sql执行输出结果进行定义,包括HashMap、基本类型、字符串类型、实体类类型,Executor在MappedStatement执行sql后将输出结果映射至Java对象中,输出结果映射过程 相当于JDBC编程中对结果的解析处理过程。
2.0.2 原理分析
-
加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加 载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配 置),存储在内存中。
-
SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、 JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根 据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
-
SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
-
结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者 基本数据类型,并将最终结果返回。
2.1 版本
-
软件版本:
-
IntelliJ IDEA:2022.1.4
-
Navicat for MySQL:16.0.14
-
MySQL数据库:8.0.30
-
-
组件版本:
-
MySQL驱动:8.0.30
-
MyBatis:3.5.10
-
JDK:Java17
-
JUnit:4.13.2
-
Logback:1.2.11
-
2.2 MyBatis下载
将框架以及框架的源码都下载下来,下载框架后解压,打开mybatis目录
-
**通过以上解压可以看到,框架一般都是以jar包的形式存在。**我们的mybatis课程使用maven,所 以这个jar我们不需要。
-
官方手册需要
2.3 MyBatis入门程序开发步骤
写代码前准备
-
准备数据库表:汽车表t_car,字段包括:
-
id:主键(自增)【bigint】
-
car_num:汽车编号【varchar】
-
brand:品牌【varchar】
-
guide_price:厂家指导价【decimal类型,专门为财务数据准备的类型】
-
produce_time:生产时间【char,年月日即可,10个长度,'2022-10-11'】
-
car_type:汽车类型(燃油车、电车、氢能源)【varchar】
-
-
使用navicat for mysql工具建表
- 使用navicat for mysql工具向t_car表中插入两条数据,如下:
- 创建Project:建议创建Empty Project,设置Java版本以及编译版本等。
- 在IDEA中集成maven
- 创建Module:创建普通的Maven Java模块
- 步骤1:在pom.xml文件中设置此项目的打包方式为:jar(不需要war,因为mybatis封装的是jdbc。)
XML
pom.xml
<!-- 当前项目的坐标-->
<groupId>org.example</groupId>
<artifactId>mybatis-001</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
- 步骤2:在pom.xml文件中引入需要的依赖jar包(mybatis依赖 + mysql驱动依赖)
XML
pom.xml
<!-- 引入mybatis的依赖-->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- 引入mysql驱动的依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
编写mybatis核心配置文件
-
步骤3:在resources根目录下 创建mybatis的核心配置文件mybatis-config.xml,这个文件主要配置连接数据库的信息、加载SQL映射文件等(可以参考mybatis手册拷贝进来)
-
注意1:其文件名可以自定义,但建议mybatis-config.xml
-
注意2:mybatis核心配置文件存放的位置也是随意的。这里选择放在resources目录下,相当于放到了类的根路径下。(resources目录相当于类的根路径)
-
XML
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 文件的根节点 -->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 四个property标签:配置连接数据库的信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bjpowernode"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--在mapper标签中配置XxxMapper.xml SQL映射文件的路径-->
<!--resource属性:指定从类的根路径下开始查找资源-->
<mapper resource="指定SQL映射文件的路径"/>
</mappers>
</configuration>
编写SQL映射文件
-
步骤4:在resources根目录下创建mapper目录,在该目录下创建 编写sql语句的sql映射文件(XxxMapper.xml),一般一张数据库表对应一个,一张数据库表对应一个实体类,一张表对应一个Mapper接口
-
注意1:sql语句最后结尾可以不写";"
-
注意2:CarMapper.xml文件的名字不固定,可以自定义。
-
注意3:CarMapper.xml文件的位置也是随意的。这里选择放在resources根下,相当于放到了类的根路径下。
-
XML
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根标签中:使用对应的标签编写对应的sql语句,namespace先随意写一个-->
<mapper namespace="car">
<!--用insert标签写insert语句:插入一条汽车信息,用其id属性指定包裹的这条sql语句的id值,id是唯一标识-->
<insert id="insertCar">
insert into t_car
(id,car_num,brand,guide_price,produce_time,car_type)
values
(null,'102','丰田mirai',40.30,'2014-10-05','氢能源')
</insert>
</mapper>
加载映射文件
-
步骤5:加载映射文件,此时Mybatis还不知道Mapper文件的存在,所以需要将 mapper映射文件的路径 配置到 mybatis-config.xml核心配置文件中(后续有新方法):
XML
<!-- 加载映射文件的位置 -->
<mappers>
<mapper resource="mapper/CarMapper.xml"/>
</mappers>
编写Mybatis程序
-
步骤6:编写MyBatis程序(MyBatisIntroductionTest代码)
-
在MyBatis中有一个专门执行sql语句的对象叫SqlSession,它是java程序和数据库之间的一次会话。
-
要获取SqlSession对象先要获取SqlSessionFactory对象,通过它来生产SqlSession对象**(一般SqlSessionFactory对象对应一个数据库)**
-
要获取SqlSessionFactory对象又要通过SqlSessionFactoryBuilder对象的build方法来获取一个SqlSessionFactory对象
-
总结mybatis的核心对象包括:SqlSessionFactoryBuilder对象->SqlSessionFactory对象->SqlSession对象
-
-
java
MyBatisIntroductionTest.java
package org.example.mybatis.test;
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.InputStream;
public class MyBatisTest {
public static void main(String[] args) throws Exception {
//1.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//创建读取Mybatis核心配置文件的流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//2.获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//3.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//4.通过SqlSession对象调用对应的方法执行 指定的sql语句
//方法传入要执行的sql语句的id值,返回值是影响数据库表中的记录数
//(4.后续:通过Mapper接口的代理类实例调用其方法执行 指定的 sql语句)
int count = sqlSession.insert("insertCar");
System.out.println("插入了几条记录" + count);
//5.手动提交事务
sqlSession.commit();
// 6.关闭资源(只关闭是不会提交的)
sqlSession.close(); //底层分析:如果使用的事务管理器是JDBC,那么底层实际上还是会执行connection.commit()
}
}
注意1:MyBatis默认采用的事务管理器是:JDBC。JDBC事务默认是不提交的,需要手动提交。
- 步骤6:运行程序,查看运行结果,以及数据库表中的数据
2.3.1 第一个MyBatis程序的细节
小技巧:凡是遇到resources这个单词,大部分情况下,加载资源的方式都是从类的根路径下开始加载资源
优点:从类路径下开始加载资源的项目 移植性很强,比如从windows移植到Linux下,代码不用修改,因为这个资源一直在类路径下
2.3.2 MyBatis的事务管理机制
为true,表示没有开启事务
2.5 MyBatis第一个比较完整的代码写法
java
MyBatisCompleteTest.java
package org.example.mybatis.test;
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;
/**
* 一个完整的MyBatis程序
*/
public class MyBatisCompleteTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
//1.获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//2.获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
//3.开启会话,获取SqlSession对象
sqlSession = sqlSessionFactory.openSession();
//4.执行SQL语句
int count = sqlSession.insert("insertCar");
System.out.println(count);
//5.提交事务
sqlSession.commit();
} catch (Exception e) {
//如果有异常就回滚事务
if (sqlSession != null){
sqlSession.rollback();
}
e.printStackTrace();
}finally{
//关闭会话
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
2.7 引入日志框架logback
下图了解即可,不用纠结。
-
引入日志框架的目的:看清楚mybatis执行的sql语句。
-
启用标准日志组件:在mybatis-config.xml文件中添加以下配置:【可参考mybatis手册】
- 标准日志也可以用,但是配置不够灵活,可以集成其他的日志组件,例如:log4j,logback等。
-
logback是目前日志框架中性能较好的,较流行的,这个是用集成的方式。
-
使用logback日志组件的步骤:
- 第一步:引入logback相关依赖
-
-
第二步:引入logback所必须的xml配置文件
-
文件名必须叫做logback.xml或logback-test.xml
-
必须放到类路径中
-
主要配置日志输出相关的级别以及日志具体的格式
-
XMLlogback.xml <?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!-- 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!-- 按照每天生成日志文件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志文件输出的文件名--> <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern> <!--日志文件保留天数--> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <!--日志文件最大的大小--> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>100MB</MaxFileSize> </triggeringPolicy> </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>
- 再次执行单元测试方法testInsertCar,查看控制台是否有sql语句输出
-
2.8 MyBatis工具类SqlSessionUtil的封装
-
目的:(使用后续的)每一次获取SqlSession对象代码太繁琐,封装一个工具类。
java
SqlSessionUtil.java
package 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;
/**
* 获取SqlSession对象的工具类
* 工具类的构造方法一般都私有化,因为工具类中的方法都是静态的,不用创建对象,直接用类名调用
*/
public class SqlSessionUtil {
private static SqlSessionFactory sqlSessionFactory = null;
private SqlSessionUtil(){}
/**
* 在静态代码块中获取SqlSessionFactory对象;类加载的时候执行获取SqlSessionFactory对象,并且只执行一次
*/
static {
try {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取执行SQL语句的SqlSession对象
* @return SqlSession对象
*/
public static SqlSession openSession(){
return sqlSessionFactory.openSession();
}
}
-
测试工具类,将testInsertCar()改造
2.9 映射文件中Mapper标签的namespace属性
SQL Mapper文件中**<mapper>标签的namespace属性** ,它主要是为了防止sql语句id冲突,其实就是SQL语句id值的前缀。(在接口代理机制中会对namespace有约束,这里随意)
创建CarMapper2.xml文件,代码如下:
不难看出,CarMapper.xml和CarMapper2.xml文件中都有 id="selectCarAll"
将CarMapper2.xml配置到mybatis-config.xml文件中。
编写Java代码如下:
运行结果如下:
Java代码修改如下:
运行结果如下:
四、MyBatis的核心配置文件详解
XML
mybatis-config.xml
<!-- 第一行是任何一个xml文件的固定写法 -->
<?xml version="1.0" encoding="UTF-8" ?>
<!-- !DOCTYPE后面这个单词是这个xml文档的根标签名 -->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd"> //.dtd:对当前XML文件的约束
<configuration>
<!-- 在environments标签中可以配置多个环境,一个environment环境对应一个数据库,通过其default属性指定:默认使用哪个环境-->
<environments default="development">
<!-- 一个environment环境对应一个SqlSessionFactory对象-->
<!-- 将来在调用sqlSessionFactoryBuilder.build(InputStream inputStream,String environment)方法的时候可以通过环境标签的id属性指定使用的是哪个数据库-->
<!--调用这个sqlSessionFactoryBuilder.build(InputStream inputStream)方法只能获取默认的环境,获取默认的sqlSessionFactory对象-->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 这四个property标签:配置连接数据库的信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bjpowernode"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 这四个property标签是配置连接数据库的信息的 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--XxxMapper.xml SQL映射文件创建好之后,将该文件的路径配置到这里-->
<!--resource属性自动从类的根路径下开始查找资源-->
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
-
configuration标签:MyBatis核心配置文件的根标签,表示配置信息。
-
mappers标签:在mappers标签中可以配置多个sql映射文件的路径。
-
mapper:配置sql映射文件的路径
-
resource属性:从类路径下开始加载资源
-
url属性:使用完全限定资源定位符(URL)方式
-
4.1 environment
mybatis-003-configuration
XML
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 在environments标签中可以配置多个环境,一个environment环境对应一个数据库,通过其default属性指定:默认使用哪个环境-->
<environments default="development">
<!-- 一个environment环境对应一个SqlSessionFactory对象-->
<!-- 将来在调用sqlSessionFactoryBuilder.build(InputStream inputStream,String environment)方法的时候可以通过环境标签的id属性指定使用的是哪个数据库-->
<!--调用这个sqlSessionFactoryBuilder.build(InputStream inputStream)方法只能获取默认的环境,获取默认的sqlSessionFactory对象-->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 这四个property标签:配置连接数据库的信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bjpowernode"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 这四个property标签是配置连接数据库的信息的 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
XML
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="car">
<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>
XML
ConfigurationTest.testEnvironment
package com.powernode.mybatis;
import com.powernode.mybatis.pojo.Car;
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 org.junit.Test;
public class ConfigurationTest {
@Test
public void testEnvironment() throws Exception{
// 准备数据
Car car = new Car();
car.setCarNum("133");
car.setBrand("丰田霸道");
car.setGuidePrice(50.3);
car.setProduceTime("2020-01-10");
car.setCarType("燃油车");
// 一个数据库对应一个SqlSessionFactory对象
// 两个数据库对应两个SqlSessionFactory对象,以此类推
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 使用默认数据库
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession(true);
int count = sqlSession.insert("insertCar", car);
System.out.println("插入了几条记录:" + count);
// 使用指定数据库
SqlSessionFactory sqlSessionFactory1 = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "dev");
SqlSession sqlSession1 = sqlSessionFactory1.openSession(true);
int count1 = sqlSession1.insert("insertCar", car);
System.out.println("插入了几条记录:" + count1);
}
}
执行结果:
-
environments标签:环境(多个),以"s"结尾表示复数,也就是说mybatis的环境可以配置多个数据源。
- default属性:通过环境environment的id指定默认使用的环境是哪个。
-
environment标签:具体的环境配置(主要包括:事务管理器的配置 + 数据源的配置)
- id属性:设置环境的唯一标识,该标识用在environments的default后面,用来指定默认使用哪个环境。
4.2 transactionManager
XML
mybatis-config2.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="dev">
<environment id="dev">
<!-- 通过其type属性指定mybatis使用什么方式管理事务(配置事务管理器) -->
<transactionManager type="MANAGED"/>
<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>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
java
ConfigurationTest.testTransactionManager
@Test
public void testTransactionManager() throws Exception{
// 准备数据
Car car = new Car();
car.setCarNum("133");
car.setBrand("丰田霸道");
car.setGuidePrice(50.3);
car.setProduceTime("2020-01-10");
car.setCarType("燃油车");
// 获取SqlSessionFactory对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config2.xml"));
// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行SQL
int count = sqlSession.insert("insertCar", car);
System.out.println("插入了几条记录:" + count);
}
transactionManager标签:通过其type属性指定mybatis使用什么方式管理事务(配置事务管理器)
-
type属性:可选值包括两个
-
**JDBC:使用JDBC原生的事务管理机制。**底层原理:事务开启conn.setAutoCommit(false); ...处理业务...事务提交conn.commit();
-
MANAGED:交给其它容器来管理事务,比如WebLogic、JBOSS等。如果没有管理事务的容器,则没有事务。没有事务的含义:只要执行一条DML语句,则提交一次。
-
当事务管理器是:JDBC
-
采用JDBC的原生事务机制:
-
开启事务:conn.setAutoCommit(false);
-
处理业务......
-
提交事务:conn.commit();
-
当事务管理器是:MANAGED
- 交给容器去管理事务,但目前使用的是本地程序,没有容器的支持,当mybatis找不到容器的支持时:没有事务。也就是说只要执行一条DML语句,则提交一次。
4.3 dataSource
-
dataSource标签:用其type属性来指定数据源的类型
-
type属性:可选值包括三个:
-
UNPOOLED :虽然也实现Javax.sql.DataSource接口,但是没有使用数据库连接池技术,每次请求过来之后,都会创建新的Connection对象
-
property标签的name可以是:
-
driver:JDBC 驱动的 Java 类全限定名。
-
url :数据库的 JDBC URL 地址。
-
username :登录数据库的用户名。
-
password :登录数据库的密码。
-
defaultTransactionIsolationLevel 默认的连接事务隔离级别。
-
defaultNetworkTimeout 等待数据库操作完成的默认网络超时时间(单位:毫秒)
-
-
-
POOLED :使用mybatis自己实现的数据库连接池,每次获取数据库连接对象都是从数据库连接池中拿
-
property标签的name可以是(除了包含UNPOOLED中之外):
-
poolMaximumActiveConnections 在任意时间可存在的活动(正在使用)连接数量,默认值:10
-
poolMaximumIdleConnections 任意时间可能存在的空闲连接数。
-
其它....
-
-
-
JNDI:集成第三方的数据库连接池
-
property可以是(最多只包含以下两个属性):
-
initial_context 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
-
data_source 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
-
-
-
-
XML
mybatis-config3.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="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<!--dataSource标签用type属性来指定数据源的类型,datasource数据源是为程序提供Connection连接对象的-->
<dataSource type="UNPOOLED">
<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>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
java
ConfigurationTest.testDataSource
@Test
public void testDataSource() throws Exception{
// 准备数据
Car car = new Car();
car.setCarNum("133");
car.setBrand("丰田霸道");
car.setGuidePrice(50.3);
car.setProduceTime("2020-01-10");
car.setCarType("燃油车");
// 获取SqlSessionFactory对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config3.xml"));
// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 执行SQL
int count = sqlSession.insert("insertCar", car);
System.out.println("插入了几条记录:" + count);
// 关闭会话
sqlSession.close();
}
当type是UNPOOLED,控制台输出:
修改配置文件mybatis-config3.xml中的配置:
XML
mybatis-config3.xml
<dataSource type="POOLED">
Java测试程序不需要修改,直接执行,看控制台输出:
通过测试得出:UNPOOLED不会使用连接池,每一次都会新建JDBC连接对象。POOLED会使用数据库连接池。【这个连接池是mybatis自己实现的。】
XML
mybatis-config3.xml
<dataSource type="JNDI">
JNDI的方式:表示对接JNDI服务器中的连接池。这种方式给了我们可以使用第三方连接池的接口。如果想使用dbcp、c3p0、druid(德鲁伊)等,需要使用这种方式。
这里再重点说一下type="POOLED"的时候,它的属性有哪些?
XML
mybatis-config3.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="dev">
<environment id="dev">
<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"/>
<!--最大连接对象活动的数量-->
<property name="poolMaximumActiveConnections" value="3"/>
<!--这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。-->
<property name="poolTimeToWait" value="20000"/>
<!--JDBC连接对象强行回归数据库连接池的时间,默认值为20秒-->
<property name="poolMaximumCheckoutTime" value="20000"/>
<!--最多空闲时连接对象的数量-->
<property name="poolMaximumIdleConnections" value="1"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
poolMaximumActiveConnections:最大连接对象活动的数量。默认值10
poolMaximumIdleConnections:最多空闲时连接对象的数量。默认值5
poolMaximumCheckoutTime:连接对象强行回归数据库连接池的时间。默认值20秒。
poolTimeToWait:当无法获取到空闲连接时,每隔20秒打印一次日志,避免因代码配置有误,导致傻等。(时长是可以配置的)
当然,还有其他属性。对于连接池来说,以上几个属性比较重要。
最大的活动的连接数量就是连接池中连接对象数量的上限。默认值10,如果有10个请求正在使用这10个连接,第11个请求只能等待空闲连接。
最大的空闲连接数量。默认值5,如何已经有了5个空闲连接,当第6个连接要空闲下来的时候,连接池会选择关闭该连接对象。来减少数据库的开销。
需要根据系统的并发情况,来合理调整连接池最大连接数以及最多空闲数量。充分发挥数据库连接池的性能。【可以根据实际情况进行测试,然后调整一个合理的数量。】
下图是默认配置:
在以上配置的基础之上,可以编写java程序测试:
java
ConfigurationTest.testPool
@Test
public void testPool() throws Exception{
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config3.xml"));
for (int i = 0; i < 4; i++) {
SqlSession sqlSession = sqlSessionFactory.openSession();
Object selectCarByCarNum = sqlSession.selectOne("selectCarByCarNum");
}
}
XML
CarMapper.xml
<select id="selectCarByCarNum" resultType="com.powernode.mybatis.pojo.Car">
select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car where car_num = '100'
</select>
4.4 .properties属性文件
mybatis提供了更加灵活的配置为:①先配置 连接数据库的信息 到一个属性文件中,假设在类的根路径下创建jdbc.properties文件,配置如下:
XML
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=123456
将来用${}通过key把对应的值从这个文件中取出来
②再在mybatis核心配置文件中用properties单标签引入属性文件,③再通过${}把属性文件中key对应的值取出来:
XML
mybatis-config4.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="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--使用${key}从xxx.properties文件中取值-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
properties标签的两个属性,在mapper标签中也有:
resource:这个属性是从类的根路径下开始加载资源。【常用的。】
url:从指定的url加载资源,假设文件放在d:/jdbc.properties,这个url可以写成:file:///d:/jdbc.properties。注意是三个斜杠哦。
注意:如果不知道mybatis-config.xml文件中标签的编写顺序的话,可以有两种方式知道它的顺序:
-
第一种方式:查看dtd约束文件。
-
第二种方式:通过idea的报错提示信息。【一般采用这种方式】
4.5 mappers标签
mappers标签中可以写多个mapper标签
mapper标签:加载SQL映射文件,包含多种配置方式,这里先主要看其中两种:
第一种:通过mapper单标签的resource属性:配置SQL映射文件的路径,表示从类的根路径下开始查找资源【比url常用】
XML
mybatis-config4.xml
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
如果是这样写的话,必须保证类的根下有CarMapper.xml文件。
如果类的根路径下有一个包叫做test,CarMapper.xml如果放在test包下的话,这个配置应该是这样写:
XML
mybatis-config4.xml
<mappers>
<mapper resource="test/CarMapper.xml"/>
</mappers>
第二种:用url属性指定SQL映射文件的路径:从指定的url位置加载
假设CarMapper.xml文件放在d盘的根下,这个配置就需要这样写:
XML
mybatis-config4.xml
<mappers>
<mapper url="file:///d:/CarMapper.xml"/>
</mappers>
后面几种:9.3
三、使用MyBatis完成CRUD
-
准备工作
-
创建Maven的普通Java模块:mybatis-002-crud
-
pom.xml
-
打包方式jar
-
引入依赖:
-
mybatis依赖
-
mysql驱动依赖
-
junit依赖
-
logback依赖
-
-
-
mybatis-config.xml放在类的根路径下
-
CarMapper.xml放在类的根路径下
-
logback.xml放在类的根路径下
-
提供自定义的com.powernode.mybatis.utils.SqlSessionUtil工具类
-
创建测试用例:com.powernode.mybatis.CarMapperTest
-
3.1 insert(Create)
-
以前SQL的映射文件中的SQL语句存在的问题
- SQL语句中的值被写死
-
SQL语句中的值不能写死,值由用户提供,动态传值。之前的JDBC代码是这样写的:
-
在MyBatis中这样写SQL语句中的值:
-
在Java程序中,先将数据封装到Map集合中
-
然后在sql语句中使用 #{} 来动态传值给SQL语句,#{} 等同于JDBC中的占位符 ? 。
-
Java程序:
-
java
package org.example.mybatis.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import utils.SqlSessionUtil;
import java.util.HashMap;
import java.util.Map;
public class TestCarMapping {
@Test
public void testInsertCar(){
//获取数据库执行对象
SqlSession sqlSession = SqlSessionUtil.openSession();
//创建Map集合,用于封装一条数据
Map<String,Object> map = new HashMap<>();
map.put("carNum",1111);
map.put("brand","老汉推车");
map.put("guidePrice",10.0);
map.put("produceTime","2020-12-12");
map.put("carType","电车");
//用SqlSession对象调用相应的方法执行指定的SQL语句,insert的第一个参数为sql语句的id值,第二个参数是封装数据的对象
int count = sqlSession.insert("insertCar",map);
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
}
-
- mapper文件:在**#{} 的里面写map集合的key。**
XML
CarMapper.xml SQL映射文件
<?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:可以配置这些SQL语句的命名空间-->
<mapper namespace="car">
<!--插入
增删改操作的标签没有resultType属性,只有查询操作才有,
因为增删改操作都是int类型的返回值,所以不需要指明
-->
<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>
- 运行测试程序,查看数据库:
如果#{}里写map集合中不存在的key会有什么问题?
如果key不存在不会报错,会在数据库表中插入NULL。
-
使用pojo对象完成传参:
-
第一步:定义一个pojo类Car,提供相关属性。
javaCar.java package org.example.mybatis.test.utils.pojo; public class Car { private Long id; private String carNum; private String brand; private Double guidePrice; private String produceTime; private String carType; public Car() { } public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) { this.id = id; this.carNum = carNum; this.brand = brand; this.guidePrice = guidePrice; this.produceTime = produceTime; this.carType = carType; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getCarNum() { return carNum; } public void setCarNum(String carNum) { this.carNum = carNum; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public Double getGuidePrice() { return guidePrice; } public void setGuidePrice(Double guidePrice) { this.guidePrice = guidePrice; } public String getProduceTime() { return produceTime; } public void setProduceTime(String produceTime) { this.produceTime = produceTime; } public String getCarType() { return carType; } public void setCarType(String carType) { this.carType = carType; } }
-
第二步:Java程序
javaTestCarMapping.java @Test public void testInsertCarPoJo(){ //获取数据库执行对象 SqlSession sqlSession = SqlSessionUtil.openSession(); //创建pojo对象,用于封装一条数据 Car car = new Car(); car.setCarNum("2000"); car.setBrand("大力"); car.setGuidePrice(30.0); car.setProduceTime("2000--3-3"); car.setCarType("电力车"); //执行SQL语句,insert的第一个参数是sql语句的id值,第二个参数是封装数据的对象 int count = sqlSession.insert("insertCar",car); System.out.println(count); sqlSession.commit(); sqlSession.close(); }
-
第三步:SQL语句,#{}里面写pojo类中的属性名,其实准确来说是get方法的方法名去掉get后,将剩下的单词首字母变小写
XMLCarMapping.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"> <!--namespace先随意写一个--> <mapper namespace="car"> <!--写insert语句:保存一个汽车信息。id属性可以指定这条sql语句的唯一标识--> <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>
-
运行程序,查看数据库表:
-
#{} 里写的是POJO的属性名,如果写成其他的会有问题吗?
错误信息中描述:在Car类中没有找到a属性的getter方法。
修改POJO类Car的代码,只将getCarNum()方法名修改为getA(),其他代码不变,如下:
再运行程序,查看数据库表中数据:
如果采用POJO对象传参,#{} 里写的是get方法的方法名去掉get之后将剩下的单词首字母变小写(例如:getAge对应的是#{age},getUserName对应的是#{userName}),如果这样的get方法不存在会报错。
注意:其实传参数的时候有一个属性parameterType,这个属性用来指定传参的数据类型,不过这个属性是可以省略的
3.2 delete(Delete)
需求:根据car_num进行删除。
mapper文件:
XML
CarMapper.xml
<delete id="deleteById">
delete from t_car where id = #{id}
</delete>
Java程序:
java
TestCarMapping.java
@Test
public void testDeleteById(){
//获取数据库执行对象
SqlSession sqlSession = SqlSessionUtil.openSession();
//执行sql语句
sqlSession.delete("deleteById",3);
sqlSession.commit();
sqlSession.close();
}
运行结果:
注意:当占位符只有一个时,#{} 里面的内容可以随便写,最好见名知意。
3.3 update(Update)
更新表中的数据:
SQL语句如下:
XML
CarMapper.xml
<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>
Java代码如下:
java
TestCarMapping.java
@Test
public void testUpdateByid(){
//获取数据库执行对象
SqlSession sqlSession = SqlSessionUtil.openSession();
//封装数据
Car car = new Car(11L,"3333","路虎",20.0D,"2003-11-30","燃料车");
//执行sql
sqlSession.update("updateById",car);
sqlSession.commit();
sqlSession.close();
}
运行结果:
当然了,如果使用map传数据也是可以的。
3.4 select(Retrieve)
select语句和其它语句的不同:它会返回一个查询结果集,要用普通对象或集合对象来接收。
查询数据时有两个点:
- 使用<select>标签的 resultType属性:指定将查询结果集 要封装成 什么类型的对象并返回 ,然后在Mybatis程序中用对应类型的对象接收查询结果(类名写全限定类名)
- 查询结果集的字段名需和java类的属性名一致,因为Mybatis要把查询到的数据 赋值(映射)到 java对象的属性中,所以要通过as关键字对查询出的列名起别名,当然还有其它解决方案,我们后面再看。
查询一条记录
需求:查询id为1的Car信息
SQL语句如下:
XML
CarMapper.xml
<select id="selectById" resultType="org.example.mybatis.test.utils.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>
Java程序如下:
java
@Test
public void testSelectById(){
//获取数据库执行对象
SqlSession sqlSession = SqlSessionUtil.openSession();
//执行sql语句
//方法的返回值:就是映射文件中配置的resultType的类型
/*
方法的执行流程:
Mybatis的底层执行器会通过sql语句的id找到对应的Statement,并把7369给占位符,执行SQL语句,得到查询结果集
Mybatis底层通过反射机制, 创建resultType属性 类型的对象 ,然后把查询得到的结果集赋值(映射)给该对象
*/
Car car = sqlSession.selectOne("selectById",1);
sqlSession.commit();
sqlSession.close();
System.out.println(car);
}
查询多条数据
需求:查询所有的Car信息。
SQL语句如下:
XML
CarMapping.xml
<!--
查询到数据返回多条记录,每一条封装在一个实体类对象中,然后将所有的实体类对象添加到List集合中
resultType:指定的并不是集合的类型,而是单条数据所对应实体类类型
resultType="java.util.List" 错误的配置方式
-->
<select id="selectByAll" resultType="org.example.mybatis.test.utils.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>
Java代码如下:
java
TestCarMapping.java
@Test
public void testSelectByAll(){
//获取数据库执行对象
SqlSession sqlSession = SqlSessionUtil.openSession();
//执行sql语句
List<Object> carList = sqlSession.selectList("selectByAll");
sqlSession.commit();
sqlSession.close();
for(Object obj:carList){
System.out.println(obj);
}
}
运行结果如下:
3.5 总结
parameterType和resultType
parameterType:指定输入参数类型(即要传递什么类型的参数给SQL语句),MyBatis会从输入对象中 取值 设置 在SQL语句中。
resultType:指定输出结果类型,即MyBatis会将Sql查询结果的一行记录 映射成 resultType指定类型的Java对象,并返回。
通用总结
-
在SQL映射文件中对应的标签中编写对应的SQL语句
-
MyBatis中都是调用SqlSession对象的对应方法来执行对应的SQL语句
-
在mybatis中,在sql语句中使用 #{} 来动态传值给SQL语句,#{} 等同于JDBC中的占位符 ?
-
如果一条数据是用Map集合封装的,在#{}里面 填写map集合的key取出对应的value传给SQL语句
-
如果一条数据是用pojp对象封装的,那么在#{}里面 根据pojo对象的属性名取出其属性值设置给SQL语句(相当于用这个符号取出传过来的pojo对象中的属性)
-
-
使用SqlSession对象调用执行sql语句的相应方法时
-
方法的第一个参数是:映射文件中sql语句的id值,通过id值指定要执行哪条SQL语句
-
方法的第二个参数:向sql语句中传入的数据,传入数据的数据类型必须与映射文件中配置的parameterType保持一致
- parameterType:输入参数的类型
-
-
执行查询语句后,会返回一个查询结果集。
- 所以必须在<select>标签中用 resultType属性:指定指定输出结果的类型(类名写全限定类名)
六、在WEB中应用MyBatis(使用MVC架构模式)
七、javassist
Mybatis接口代理机制的原理,初学不用管
八、MyBatis中的接口代理机制
8.1 Mybatis接口代理机制的理解
- 为什么用接口代理机制?:因为dao实现类中的方法很固定,基本上就是一行代码: 通过SqlSession对象调用insert、delete、update、select等方法,这个类中的方法没有任何业务逻辑,所以使用接口代理机制动态生成dao接口的实现类
- MyBatis中的接口代理机制:动态生成DAO接口的实现类(代理类)并创建其实例对象,以后获取到其代理实现类对象直接用即可。
- mybatis实际上使用了代理模式,在内存中生成dao接口的代理类,并创建代理类的实例。
8.2 接口代理机制的开发规范
编写Mapper接口需要遵循一些规范,MyBatis就可以自动生成Mapper接口的实现类代理对象。
使用接口代理机制的前提:
- SQL映射文件中mapper标签的namespace属性需指定为:Mapper接口的全限定名
- 将mapper文件和Mapper接口关联起来,将来生成的Mapper接口实现类对象调用方法时,才知道执行的是哪个映射文件中的哪个sql语句
- SQL映射文件,sql语句的id值要和Mapper接口中的方法名一致
- Mapper.xml中定义的每个sql的parameterType的类型与Mapper接口方法的参数类型相同。
- Mapper.xml中定义的每个sql的resultType的类型与Mapper接口方法返回值类型相同。
- 注:Mapper.xml映射文件最好和Mapper接口名称一致。
如何获取Mapper接口的代理类对象:通过SqlSession对象调用其getMapper()方法,传入Mapper接口的字节码文件对象,指定获取哪个Mapper接口的代理类对象
java
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
8.3 接口代理机制实例(005-crud2)
面向接口进行crud
和以前的不同点:只有Mapper文件变了,和不用写Mapper接口的实现类了。
和以前的区别:
- 一个是在Mapper接口的实现类中编写SqlSession对象来执行SQL语句;
- 一个是直接在测试程序中用Mapper接口的代理类对象来执行SQL语句
- 后者在调用相应方法执行时,不用指定SQL语句的id,因为SQL语句的id已经规范为方法名了,Mybatis的接口代理机制会自动替你完成指定,你调用哪个方法就是在指定执行哪条SQL语句
java
CarMapper接口
CarMapper.java
package org.example.mapper;
import org.example.pojo.Car;
import java.util.List;
public interface CarMapper {
/**
* 插入一条car记录
* @param car
* @return
*/
int insert(Car car);
/**
* 根据id删除car记录
* @param id
* @return
*/
int deleteById(Long id);
/**
* 更新汽车信息
* @param car
* @return
*/
int update(Car car);
/**
* 根据id查询
* @param id
* @return
*/
Car selectById(Long id);
/**
* 查询所有car的记录
* @return
*/
List<Car> selectAll();
}
java
SQL映射文件
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">
<!--要想使用接口代理机制:sql映射文件中mapper标签的namespace必须是Mapper接口的全限定名,sql语句的id值也必须是dao接口的方法名-->
<mapper namespace="org.example.mapper.CarMapper">
<insert id="insert">
insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>
<delete id="deleteById">
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="org.example.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="org.example.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>
java
TestCarMapper.java
package test;
import org.apache.ibatis.session.SqlSession;
import org.example.mapper.CarMapper;
import org.example.pojo.Car;
import org.example.utils.SqlSessionUtil;
import org.junit.Test;
import java.util.List;
public class TestCarMapper {
@Test
public void testInsert(){
SqlSession sqlSession = SqlSessionUtil.openSession();
//获取Mapper接口的代理类对象,传入Mapper接口的字节码文件对象,告诉mybatis生成哪个Mapper接口的代理类对象
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
//通过CarMapper接口的代理类对象调用其中的方法执行SQL语句
int count = mapper.insert(new Car(null,"7987","小车车",32.0,"2003-11-04","新能源"));
System.out.println(count);
sqlSession.commit();
SqlSessionUtil.close(sqlSession);
}
@Test
public void testdeleteById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
//获取DAO接口的代理类对象
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
int count = mapper.deleteById(17L);
sqlSession.commit();
SqlSessionUtil.close(sqlSession);
}
@Test
public void testupdate(){
SqlSession sqlSession = SqlSessionUtil.openSession();
//获取DAO接口的代理类对象
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
int count = mapper.update(new Car(18L,"456","大车车",36.0,"2003-11-04","新能源"));
System.out.println(count);
sqlSession.commit();
SqlSessionUtil.close(sqlSession);
}
@Test
public void selectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
//获取DAO接口的代理类对象
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = mapper.selectById(1L);
System.out.println(car);
SqlSessionUtil.close(sqlSession);
}
@Test
public void testselectAll(){
SqlSession sqlSession = SqlSessionUtil.openSession();
//获取DAO接口的代理类对象
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAll();
System.out.println(cars);
SqlSessionUtil.close(sqlSession);
}
}
九、总结Mybatis中DAO(Mapper)的开发方式
十、Mybatis核心配置文件的剩余标签
十一、#{}和${}的区别
-
#{}:底层使用的是PreparedStatement对象。它是先编译sql语句,再给占位符传值。可以防止sql注入,比较常用。
-
${}:底层使用的是Statement对象。它先拼接sql语句,再编译sql语句。存在sql注入的风险。只有在需要进行sql语句关键字拼接的情况下才会用到。
-
表面区别:#{}传进去的值有单引号,${}传进去的值没有加单引号
-
优先使用#{},避免SQL注入
需求:根据car_type查询汽车
模块名:mybatis-005-antic
使用#{}
导入依赖
XML
pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.powernode</groupId>
<artifactId>mybatis-005-antic</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!--mysql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!--logback依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
jdbc.properties放在类的根路径下
XML
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=root
logback.xml,可以拷贝之前的,放到类的根路径下
utils:
java
SqlSessionUtil.java
package com.powernode.mybatis.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;
/**
* MyBatis工具类
*
* @author 老杜
* @version 1.0
* @since 1.0
*/
public class SqlSessionUtil {
private static SqlSessionFactory sqlSessionFactory;
/**
* 类加载时初始化sqlSessionFactory对象
*/
static {
try {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (Exception e) {
e.printStackTrace();
}
}
private static ThreadLocal<SqlSession> local = new ThreadLocal<>();
/**
* 每调用一次openSession()可获取一个新的会话,该会话支持自动提交。
*
* @return 新的会话对象
*/
public static SqlSession openSession() {
SqlSession sqlSession = local.get();
if (sqlSession == null) {
sqlSession = sqlSessionFactory.openSession();
local.set(sqlSession);
}
return sqlSession;
}
/**
* 关闭SqlSession对象
* @param sqlSession
*/
public static void close(SqlSession sqlSession){
if (sqlSession != null) {
sqlSession.close();
}
local.remove();
}
}
pojo类
java
Car.java
package com.powernode.mybatis.pojo;
/**
* 普通实体类:汽车
* @author 老杜
* @version 1.0
* @since 1.0
*/
public class Car {
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
// 构造方法
// set get方法
// toString方法
}
mapper接口(DAO接口)
java
CarMapper.java
package com.powernode.mybatis.mapper;
import com.powernode.mybatis.pojo.Car;
import java.util.List;
/**
* Car的sql映射对象
* @author 老杜
* @version 1.0
* @since 1.0
*/
public interface CarMapper {
/**
* 根据car_num获取Car
* @param carType
* @return
*/
List<Car> selectByCarType(String carType);
}
mybatis-config.xml,放在类的根路径下
XML
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="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
CarMapper.xml,放在类的根路径下:注意namespace必须和接口名一致。id必须和接口中方法名一致。
XML
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">
<select id="selectByCarType" 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 = #{carType}
</select>
</mapper>
测试程序
java
CarMapperTest
package com.powernode.mybatis.test;
import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.junit.Test;
import java.util.List;
/**
* CarMapper测试类
* @author 老杜
* @version 1.0
* @since 1.0
*/
public class CarMapperTest {
@Test
public void testSelectByCarType(){
CarMapper mapper = (CarMapper) SqlSessionUtil.openSession().getMapper(CarMapper.class);
List<Car> cars = mapper.selectByCarType("燃油车");
cars.forEach(car -> System.out.println(car));
}
}
执行结果:
通过执行可以清楚的看到,sql语句中是带有 ? 的,这个 ? 就是大家在JDBC中所学的占位符,专门用来接收值的。
把"燃油车"以String类型的值,传递给 ?
结论:这就是 #{},它会先进行sql语句的预编译,然后再给占位符传值
使用${}
同样的需求,我们使用${}来完成
CarMapper.xml文件修改如下:
XML
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">
<select id="selectByCarType" 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 = #{carType}-->
car_type = ${carType}
</select>
</mapper>
再次运行测试程序:
出现异常了,这是为什么呢?看看生成的sql语句:
结论:很显然,${} 是先进行sql语句的拼接,然后再编译,出现语法错误是正常的,因为 燃油车 是一个字符串,在sql语句中应该添加单引号
修改:
XML
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">
<select id="selectByCarType" 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 = #{carType}-->
<!--car_type = ${carType}-->
car_type = '${carType}'
</select>
</mapper>
再执行测试程序:
通过以上测试,可以看出,对于以上这种需求来说,还是建议使用 #{} 的方式。
原则:能用 #{} 就不用 ${}
什么情况下必须使用${}
在向sql语句传SQL语句关键字的时候需要使用{},因为#{}是以值的形式放到SQL语句中的。必须使用{}
需求:通过向sql语句中注入asc或desc关键字,来完成数据的升序或降序排列。
- 先使用#{}尝试:
CarMapper接口:
java
CarMapper
/**
* 查询所有的Car
* @param ascOrDesc asc或desc
* @return
*/
List<Car> selectAll(String ascOrDesc);
CarMapper.xml文件:
XML
CarMapper.xml
<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
order by carNum #{key}
</select>
测试程序
java
CarMapperTest.testSelectAll
@Test
public void testSelectAll(){
CarMapper mapper = (CarMapper) SqlSessionUtil.openSession().getMapper(CarMapper.class);
List<Car> cars = mapper.selectAll("desc");
cars.forEach(car -> System.out.println(car));
}
运行:
报错的原因是sql语句不合法,因为采用这种方式传值,最终sql语句会是这样:
select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType from t_car order by carNum 'desc'
desc是一个关键字,不能带单引号的,所以在进行sql语句关键字拼接的时候,必须使用${}
- 使用${} 改造
XML
CarMapper.xml
<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
<!--order by carNum #{key}-->
order by carNum ${key}
</select>
再次执行测试程序:
向SQL语句中拼接表名
现实业务中,可能存在分表存储数据的情况。因为表存的话,数据量太大,查询效率比较低。可以将这些数据有规律的分表存储,这样查询效率就比较高,因为扫描的数据量就变少了
业务背景:实际开发中,有的表数据量非常庞大,可能会采用分表方式进行存储,比如每天生成一张表,表的名字与日期挂钩,例如:2022年8月1日生成的表叫:t_user20220108。2000年1月1日生成的表叫:t_user20000101。此时前端在进行查询的时候会提交一个日期,比如前端提交的日期为:2000年1月1日,那么后端就会把这个日期和表名拼接为:t_user20000101 。**有了这个表名之后,将表名传到sql语句当中,返回查询结果。**那么大家思考一下,拼接表名到sql语句当中应该使用#{} 还是 ${} 呢?
使用#{}会是这样:select * from 't_car'
使用${}会是这样:select * from t_car
XML
LogMapper.xml
<select id="selectAllByTableName" resultType="car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_log_
</select>
java
LogMapper.java
public interface LogMapper{
/**
* 根据日期查询不同的表,获取表中所有的日志
* @param tableName
* @return
*/
List<Log> selectAllByTable(String date);
}
java
public class TestLogMapper{
@Test
public void testSelectAllByTable(){
SqlSession sqlSession = SqlSessionUtil.openSqlSession();
LogMapper mapper = sqlSession.getMapper(LogMapper.class);
List<Log> logs = mapper.selectAllByTable("20220901");
logs.forEach(log -> System.out.println(log));
}
}
执行结果:
批量删除
批量删除:一次删除多条记录。
批量删除的SQL语句有两种写法:
-
第一种or:delete from t_user where id = 1 or id = 2 or id = 3;
-
第二种in:delete from t_user where id in(1, 2, 3);
现在使用第二种in的方式处理,前端传过来的字符串:"1, 2, 3"
如果使用mybatis处理,应该使用#{} 还是 ${}
使用#{} :delete from t_user where id in('1,2,3') 执行错误:1292 - Truncated incorrect DOUBLE value: '1,2,3'
使用{} :delete from t_user where id in(1, 2, 3)应该采用{}来取值
java
CarMapper接口
/**
* 根据id批量删除
* @param ids
* @return
*/
int deleteBatch(String ids);
XML
CarMapper.xml
<delete id="deleteBatch">
delete from t_car where id in(${ids})
</delete>
java
CarMapperTest.testDeleteBatch
@Test
public void testDeleteBatch(){
CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
int count = mapper.deleteBatch("1,2,3");
System.out.println("删除了几条记录:" + count);
SqlSessionUtil.openSession().commit();
}
执行结果:
模糊查询
模糊查询的三种方式
1、#{}占位符,防止sql注入
需要在Java中将传入数据的前后拼接%符号
where ename like #{ename}
2、使用字符串拼接函数
where ename like concat('%',#{ename},'%')
3、${}拼接符号,实现sql的拼接
where ename like '%${value}%'
注意:{}不是占位符,如果输入参数为简单类型,{}中的内容必须为value
第一种方式:
mapper文件:
XML
<select id="selectByEname1" parameterType="java.lang.String"
resultType="com.gs.entity.Emp">
select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
where ename like #{ename}
</select>
Java代码:
java
@Test
public void testSelectByEname() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
String ename = "S";
List<Emp> list = session.selectList("emp.selectByEname1", "%"+ename+"%");
for (Emp emp : list) {
System.out.println(emp);
}
session.close();
}
第二种方式:
mapper文件:
XML
<select id="selectByEname2" parameterType="java.lang.String"
resultType="com.gs.entity.Emp">
select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
where ename like concat('%',#{ename},'%')
</select>
Java代码:
java
@Test
public void testSelectByEname2() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
String ename = "S";
List<Emp> list = session.selectList("emp.selectByEname2", ename);
for (Emp emp : list) {
System.out.println(emp);
}
session.close();
}
第三种方式:
mapper文件:
XML
<select id="selectByEname3" parameterType="java.lang.String"
resultType="com.gs.entity.Emp">
select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
where ename like '%${value}%'
</select>
Java代码:
java
@Test
public void testSelectByEname3() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
String ename = "S";
List<Emp> list = session.selectList("emp.selectByEname3", ename);
for (Emp emp : list) {
System.out.println(emp);
}
session.close();
}