文章目录
- 课件&资料
- Mybatis下载
- 了解Mybatis
- ORM思想
- Mybatis与ORM
- MyBatis框架特点
- Mybatis入门程序
- 数据库表的准备
- 创建Project
- 创建Module
- 设置打包⽅式
- 引⼊依赖
- 编写mybatis核心配置文件:mybatis-config.xml
- mybatis中有两个主要的配置文件:
- 编写XxxxMapper.xml文件
- 在mybatis-config.xml文件中指定XxxxMapper.xml文件的路径:
- 编写MyBatis程序。
- 关于第一个程序的小细节
- 关于mybatis的事务管理机制。(深度剖析)
- MyBatis第⼀个⽐较完整的代码写法
- 引⼊JUnit
- JUnit的使用
- 关于mybatis集成日志组件。
- 集成logback日志框架。
- MyBatis⼯具类SqlSessionUtil的封装
- 使用MyBatis完成CRUD
- insert
- 使用Map集合传参
- 使用POJO(简单普通的java对象)传参
- delete
- update
- select
- 查一条数据
- 查多条数据
- SQL Mapper的namespace
- MyBatis核心配置文件详解
- 手写MyBatis框架
- dom4j解析XML文件
- GodBatis
- 在WEB中应用MyBatis(使用MVC架构模式)
- 需求描述
- 数据库表的设计和准备数据
- 环境搭建
- 前端页面index.html
- 创建pojo包、service包、dao包、web包、utils包(MVC架构模式)
- 定义pojo类:Account
- 后端代码实现
- 使用ThreadLocal进行事务控制
- MyBatis核心对象的作用域
- 使用javassist生成类 & MyBatis中接口代理机制及使用
- Javassist的使用
- 使用javassist动态生成类并实现接口
- 工具类GenerateDaoProxy的实现
- 工具类GenerateDaoProxy测试与Mybatis的getMapper方法
- 面向接口进行CRUD
- MyBatis小技巧
- #{}和${}
- 向SQL语句当中拼接表名
- 批量删除:一次删除多条记录。
- 模糊查询:like
- 别名机制
- typeAliases
- package
- mybatis-config.xml文件中的mappers标签。
- idea配置文件模板
- 插入数据时获取自动生成的主键
- MyBatis参数处理
- 单个简单类型参数
- Map集合
- 实体类参数
- 多参数
- @Param注解(命名参数)
- @Param源码分析
- MyBatis查询语句专题
- 返回Map
- 返回`List
- 返回Map
- resultMap结果映射
- 使用resultMap进行结果映射
- 开启驼峰命名自动映射
- 返回总记录条数
- 动态SQL
- if标签
- where标签
- trim标签
- set标签
- choose when otherwise
- foreach标签
- sql标签与include标签
- MyBatis的高级映射及延迟加载
- 多对一
- 第一种方式:级联属性映射
- 第二种方式:association
- 第三种方式:分步查询 & 多对一延迟加载
- 一对多
- 第一种方式:collection
- 第二种方式:分步查询 & 一对多延迟加载
- MyBatis的缓存
- 一级缓存
- 二级缓存
- MyBatis集成EhCache
- MyBatis的逆向工程
- 逆向工程的使用(基础版)
- 逆向工程的使用(增强版)
- MyBatis使用PageHelper
- limit分页
- PageHelper插件
- MyBatis的注解式开发
- @Insert
- @Delete
- @Update
- @Select
- @Results
课件:老杜MyBatis–原版:【https://www.yuque.com/zuihoudewu/java_note/mt2812】
资料:【https://pan.baidu.com/s/1SsN1Zbs-VrJBqlLbvL5k7g?pwd=1234 】
提取码:1234
【Mybatis github地址:https://github.com/mybatis/mybatis-3】
【Mybatis中文使用手册:Mybatis中文网 https://mybatis.net.cn/】
# 创建数据库
CREATE DATABASE mybatis_study;
# 使用数据库
USE mybatis_study;
# 创建表 汽⻋表t_car
CREATE TABLE t_car(
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '自然主键',
car_num VARCHAR(255) COMMENT '汽车编号',
brand VARCHAR(255) COMMENT '汽车品牌',
guide_price DECIMAL(10, 2) COMMENT '厂家指导价',
produce_time CHAR(10) COMMENT '生产时间 如:2022-10-11',
car_type VARCHAR(255) COMMENT '汽车类型'
);
# 添加数据
INSERT INTO t_car(car_num, brand, guide_price, produce_time, car_type)
VALUES ('1001', '宝马520Li', 10.00, '2022-10-11', '燃油车'),
('1002', '奔驰E300L', 55.00, '2022-11-11', '新能源');
建议创建Empty Project,设置Java版本以及编译版本等。
普通的Maven Java模块
resources用于存放配置文件或资源文件,放在该目录下的文件,相当于放到了类的根路径下。
不管是maven还是普通的java模块,只要是idea文件夹中变成蓝色的部分,就是可以理解为类路径的起始地点(之后的编译就是由蓝色文件夹开始算类路径)
自定义文件夹后用idea标记为resource后的文件夹也会被最终打包到类路径下
设置打包⽅式为jar(不需要war,因为mybatis封装的是jdbc。)
<packaging>jarpackaging>
mybatis依赖 + mysql驱动依赖
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.30version>
dependency>
dependencies>
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/po
wernode"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource=""/>
mappers>
configuration>
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,'102','丰⽥mirai',40.30,'2014-10-05','氢能源')
insert>
mapper>
<mappers>
<mapper resource="CarMapper.xml"/>
mappers>
使用mybatis的类库,编写mybatis程序,连接数据库,做增删改查就行了。
在MyBatis当中,负责执行SQL语句的那个对象叫做什么呢?
mybatis的核心对象包括:
package cw.mybatis;
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{
// 获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 获取SqlSessionFactory对象
InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); // Resources.getResourceAsStream默认就是从类的根路径下开始查找资源。
//InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); // 需要传入核心配置文件对应的输入流,一般情况下都是一个数据库对应一个SqlSessionFactory对象。
// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行SQL语句
int count = sqlSession.insert("insertCar"); // 传入SQL语句对应的id,返回值是影响数据库表当中的记录条数。
System.out.println("插入了几条记录:" + count);
// 手动提交
sqlSession.commit();
}
}
Resources.getResourceAsStream
InputStream is = new FileInputStream("d:\\mybatis-config.xml");
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
,底层的源代码其实就是:InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
resource属性:这种方式是从类路径当中加载资源。
url属性:这种方式是从绝对路径当中加载资源。(绝对路径前需要加上file:///
)
// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(); // 如果使用的事务管理器是JDBC的话,底层实际上会执行:conn.setAutoCommit(false);
// 执行SQL语句
int count = sqlSession.insert("insertCar"); // 返回值是影响数据库表当中的记录条数。
System.out.println("插入了几条记录:" + count);
// 手动提交
sqlSession.commit(); // 如果使用的事务管理器是JDBC的话,底层实际上还是会执行conn.commit();
SqlSession sqlSession = sqlSessionFactory.openSession(true);
,表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false);
执行conn.setAutoCommit(false);
那么autoCommit就是true。如果autoCommit是true,就表示没有开启事务(事务自动提交)。只要执行任意一条DML语句就提交一次。conn.setAutoCommit(false);
的话,默认的autoCommit是true。package com.powernode.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;
public class MyBatisCompleteTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
// 开启会话(底层会开启事务)
sqlSession = sqlSessionFactory.openSession();
// 执行SQL语句,处理相关业务
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();
}
}
}
}
第⼀步:引⼊依赖
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
第⼆步:编写单元测试类【测试⽤例】,测试⽤例中每⼀个测试⽅法上使⽤@Test注解进⾏标注
package com.powernode.junit.service;
import org.junit.Assert;
import org.junit.Test;
public class MathServiceTest { // 名字规范:你要测试的类名+Test
// 单元测试方法写多少个。
// 一般是一个业务方法对应一个测试方式。
// 测试方法的规范: public void testXxxx(){}
// 测试方法的方法名:以test开始。假设测试的方法是sum,这个测试方法名:testSum
// @Test注解非常重要,被这个注解标注的方法就是一个单元测试方法。
@Test
public void testSum(){
// 单元测试中有两个重要的概念:
// 一个是:实际值(被测试的业务方法的真正执行结果)
// 一个是:期望值(执行了这个业务方法之后,你期望的执行结果是多少)
MathService mathService = new MathService();
// 获取实际值
int actual = mathService.sum(1, 2);
// 期望值
//int expected = 3;
int expected = 30;
// 加断言进行测试
Assert.assertEquals(expected, actual);
}
@Test
public void testSub(){
MathService mathService = new MathService();
// 实际值
int actual = mathService.sub(10, 5);
// 期望值
int expected = 5;
// 添加断言机制
Assert.assertEquals(expected, actual);
}
}
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
logback日志框架实现了slf4j标准。(沙拉风:日志门面。日志标准。)
使用标准日志STDOUT_LOGGING之外的其他日志框架,可以不在mybatis的配置文件中指定 MyBatis 所用日志的具体实现,未指定时将会自动查找,只需要引入相应日志框架的依赖即可。
第一步:引入logback的依赖。
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.11version>
dependency>
第二步:引入logback所必须的xml配置文件。
这个配置文件的名字必须叫做:logback.xml或者logback-test.xml,不能是其它的名字。
这个配置文件必须放到类的根路径下。不能是其他位置。
<configuration debug="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>[%thread] %-5level %logger{50} - %msg%npattern>
encoder>
appender>
<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"/>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
root>
configuration>
package cw.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;
import java.io.IOException;
/**
* Mybatis工具类
*/
public class SqlSessionUtils {
// 工具类的构造方法一般为私有,防止实例化对象
// 工具类中的方法都是静态的,可以直接采用类名进行调用,不需要new对象,方便调用
private SqlSessionUtils() {}
private static SqlSessionFactory sqlSessionFactory;
// 类加载时执行
// SqlSessionUtil工具类在进行第一次加载的时候,解析mybatis-config.xml文件。创建SqlSessionFactory对象。
static {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// SqlSessionFactory对象:一个SqlSessionFactory对应一个environment,一个environment通常是一个数据库。
try {
sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 获取数据库会话对象
* @return 数据库会话对象
*/
public static SqlSession openSession() {
// SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// SqlSessionFactory对象:一个SqlSessionFactory对应一个environment,一个environment通常是一个数据库。
// SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}
测试:
package cw.mybatis.test;
import cw.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class SqlSessionUtilsTest {
@Test
public void testOpenSession() {
SqlSession sqlSession = SqlSessionUtils.openSession();
int count = sqlSession.insert("insertCar");
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
}
<mapper namespace="car">
<insert id="insertCar">
insert into t_car(car_num,brand,guide_price,produce_time,car_type)
values('103', '奔驰E300L', 50.3, '2022-01-01', '燃油车')
insert>
mapper>
JDBC的代码是怎么写的?
String sql = "insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)";
ps.setString(1, xxx);
ps.setString(2, yyy);
....
<mapper namespace="car">
<insert id="insertCar">
insert into t_car(car_num,brand,guide_price,produce_time,car_type)
values(#{}, #{}, #{}, #{}, #{})
insert>
mapper>
java程序中使用Map可以给SQL语句的占位符传值,占位符中写map集合的key,如果key不存在,获取的是null,一般map集合的key起名的时候要见名知意。
<mapper namespace="car">
<insert id="insertCar">
insert into t_car
(id,car_num,brand,guide_price,produce_time,car_type)
values
(null, #{k1}, #{k2}, #{k3}, #{k4}, #{k5})
insert>
mapper>
package cw.mybatis;
import cw.mybatis.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.HashMap;
import java.util.Map;
public class CarMapperTest {
@Test
public void testInsertCar() {
// 使用map集合进行数据的封装。
Map<String, Object> map = new HashMap<>();
map.put("k1", "1111");
map.put("k2", "比亚迪汉");
map.put("k3", 10.0);
map.put("k4", "2020-11-11");
map.put("k5", "电车");
SqlSession sqlSession = SqlSessionUtils.openSession();
// 执行SQL语句
// insert方法的参数:
// 第一个参数:sqlId,从CarMapper.xml文件中复制。
// 第二个参数:封装数据的对象。
sqlSession.insert("insertCar", map);
sqlSession.commit();
sqlSession.close();
}
}
第一步:定义一个pojo类Car,提供相关属性
package cw.mybatis.pojo;
/**
* 封装汽车相关信息的pojo类。普通的java类。
*/
public class Car {
// 数据库表当中的字段应该和pojo类的属性一一对应。
// 建议使用包装类,这样可以防止null的问题。
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;
}
@Override
public String toString() {
return "Car{" + "id=" + id + ", carNum='" + carNum + '\'' + ", brand='" + brand + '\'' + ", guidePrice=" + guidePrice + ", produceTime='" + produceTime + '\'' + ", carType='" + carType + '\'' + '}';
}
}
第二步:Java程序
@Test
public void testInsertCarPOJO() {
// 封装数据
Car car = new Car(null, "3333", "比亚迪", 30.0, "2020-11-11", "新能源");
SqlSession sqlSession = SqlSessionUtils.openSession();
// 执行sql
sqlSession.insert("insertCar", car);
sqlSession.commit();
sqlSession.close();
}
第三步:SQL语句
<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>
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values(null,#{xyz},#{brand},#{guidePrice},#{produceTime},#{carType})
需求:根据id删除数据
实现:
SQL语句
<delete id="deleteById">
delete from t_car where id = #{id}
delete>
java程序
@Test
public void testDeleteById() {
SqlSession sqlSession = SqlSessionUtils.openSession();
// 第二个参数会被自动装箱成相应的类型
sqlSession.delete("deleteById", 10);
sqlSession.commit();
sqlSession.close();
}
注意:如果占位符只有一个,那么#{}的大括号里可以随意。但是最好见名知意。
需求:根据id修改某条记录。
<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>
@Test
public void testUpdateById() {
SqlSession sqlSession = SqlSessionUtils.openSession();
// 封装数据
Car car = new Car(4L, "9999", "凯美瑞", 30.3, "1999-11-10", "燃油车");
int count = sqlSession.update("updateById", car);
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
需求:根据id查询。
<select id="selectById" resultType="cw.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>
@Test
public void testSelectById() {
SqlSession sqlSession = SqlSessionUtils.openSession();
// 执行DQL语句。查询。根据id查询。返回结果一定是一条。
// mybatis底层执行了select语句之后,一定会返回一个结果集对象:ResultSet
// JDBC中叫做ResultSet,接下来就是mybatis应该从ResultSet中取出数据,封装java对象。
Object car = sqlSession.selectOne("selectById", 1);
System.out.println(car);
sqlSession.close();
}
<select id="selectAll" resultType="cw.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>
@Test
public void testSelectAll() {
SqlSession sqlSession = SqlSessionUtils.openSession();
List<Car> cars = sqlSession.selectList("selectAll");
cars.forEach(System.out::println);
sqlSession.close();
}
在sql mapper.xml文件当中有一个namespace,这个属性是用来指定命名空间的。用来防止id重复。
实际上,本质上,mybatis中的sqlId的完整写法:namespace.id
创建CarMapper2.xml文件,代码如下:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="car2">
<select id="selectCarAll" 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>
CarMapper.xml和CarMapper2.xml文件中都有 id=“selectCarAll”
将CarMapper2.xml配置到mybatis-config.xml文件中。
<mappers>
<mapper resource="CarMapper.xml"/>
<mapper resource="CarMapper2.xml"/>
mappers>
Java代码
@Test
public void testSelectAll() {
SqlSession sqlSession = SqlSessionUtils.openSession();
List<Car> cars = sqlSession.selectList("selectAll");
cars.forEach(System.out::println);
sqlSession.close();
}
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.IllegalArgumentException:
selectCarAll is ambiguous in Mapped Statements collection (try using the full name including the namespace, or rename one of the entries)
【翻译】selectCarAll在Mapped Statements集合中不明确(请尝试使用包含名称空间的全名,或重命名其中一个条目)
【大致意思是】selectCarAll重名了,你要么在selectCarAll前添加一个名称空间,要有你改个其它名字。
Java代码修改如下:
@Test
public void testSelectAll() {
SqlSession sqlSession = SqlSessionUtils.openSession();
List<Car> cars = sqlSession.selectList("car.selectAll");
cars.forEach(System.out::println);
sqlSession.close();
}
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="powernodeDB">
<environment id="powernodeDB">
<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}"/>
<property name="poolMaximumActiveConnections" value="10"/>
<property name="poolTimeToWait" value="2000"/>
<property name="poolMaximumCheckoutTime" value="10000"/>
<property name="poolMaximumIdleConnections" value="5"/>
dataSource>
environment>
<environment id="mybatisDB">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="CarMapper.xml"/>
mappers>
configuration>
引入dom4j的依赖
<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.0modelVersion>
<groupId>org.groupgroupId>
<artifactId>parse-xml-by-dom4jartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>jarpackaging>
<dependencies>
<dependency>
<groupId>org.dom4jgroupId>
<artifactId>dom4jartifactId>
<version>2.1.3version>
dependency>
<dependency>
<groupId>jaxengroupId>
<artifactId>jaxenartifactId>
<version>1.2.0version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
dependencies>
<properties>
<maven.compiler.source>17maven.compiler.source>
<maven.compiler.target>17maven.compiler.target>
properties>
project>
mybatis核心配置文件mybatis-config.xml:
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/mybatis_study"/>
<property name="username" value="root"/>
<property name="password" value="123123"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="CarMapper.xml"/>
mappers>
configuration>
CarMapper.xml
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,'102','丰⽥mirai',40.30,'2014-10-05','氢能源')
insert>
mapper>
package com.powernode.xml.test;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class ParseXMLByDom4jTest {
@Test
public void testParseMyBatisConfigXML() throws Exception{
// 创建SAXReader对象
SAXReader reader = new SAXReader();
// 获取输入流
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
// 读XML文件,返回document对象。document对象是文档对象,代表了整个XML文件。
Document document = reader.read(is);
// 获取文档当中的根标签
//Element rootElt = document.getRootElement();
//String rootEltName = rootElt.getName();
//System.out.println("根节点的名字:" + rootEltName);
//获取default默认的环境id
// xpath是做标签路径匹配的。能够让我们快速定位XML文件中的元素。
// 以下的xpath代表了:从根下开始找configuration标签,然后找configuration标签下的子标签environments
String xpath = "/configuration/environments";
Element environments = (Element) document.selectSingleNode(xpath); // Element是Node类的子类,方法更多,使用更便捷。
// 获取属性的值
String defaultEnvironmentId = environments.attributeValue("default");
//System.out.println("默认环境的id:" + defaultEnvironmentId);
// 获取具体的环境environment
xpath = "/configuration/environments/environment[@id='"+defaultEnvironmentId+"']";
//System.out.println(xpath);
Element environment = (Element) document.selectSingleNode(xpath);
// 获取environment节点下的transactionManager节点(Element的element()方法用来获取孩子节点)
Element transactionManager = environment.element("transactionManager");
String transactionType = transactionManager.attributeValue("type");
System.out.println("事务管理器的类型:" + transactionType);
// 获取dataSource节点
Element dataSource = environment.element("dataSource");
String dataSourceType = dataSource.attributeValue("type");
System.out.println("数据源的类型:" + dataSourceType);
// 获取dataSource节点下的所有子节点
List<Element> propertyElts = dataSource.elements();
// 遍历
propertyElts.forEach(propertyElt -> {
String name = propertyElt.attributeValue("name");
String value = propertyElt.attributeValue("value");
System.out.println(name + "=" + value);
});
// 获取所有的mapper标签
// 不想从根下开始获取,你想从任意位置开始,获取所有的某个标签,xpath该这样写
xpath = "//mapper";
List<Node> mappers = document.selectNodes(xpath);
// 遍历
mappers.forEach(mapper -> {
Element mapperElt = (Element) mapper;
String resource = mapperElt.attributeValue("resource");
System.out.println(resource);
});
}
@Test
public void testParseSqlMapperXML() throws Exception{
SAXReader reader = new SAXReader();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("CarMapper.xml");
Document document = reader.read(is);
// 获取namespace
String xpath = "/mapper";
Element mapper = (Element) document.selectSingleNode(xpath);
String namespace = mapper.attributeValue("namespace");
System.out.println(namespace);
// 获取mapper节点下所有的子节点
List<Element> elements = mapper.elements();
// 遍历
elements.forEach(element -> {
// 获取sqlId
String id = element.attributeValue("id");
System.out.println(id);
// 获取resultType
String resultType = element.attributeValue("resultType"); // 没有这个属性的话,会自动返回"null"
System.out.println(resultType);
// 获取标签中的sql语句(表示获取标签中的文本内容,而且去除前后空白)
String sql = element.getTextTrim();
System.out.println(sql);
// insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
// insert into t_car values(null,?,?,?,?,?)
// mybaits封装了jdbc。早晚要执行带有?的sql语句。
// 转换
String newSql = sql.replaceAll("#\\{[0-9A-Za-z_$]*}", "?");
System.out.println(newSql);
});
}
}
GodBatis:https://www.yuque.com/zuihoudewu/java_note/mt2812#g4aW0
USE dbtest;
DROP TABLE IF EXISTS t_act;
CREATE TABLE t_act (
id int PRIMARY KEY AUTO_INCREMENT,
actno VARCHAR(255),
balance DECIMAL(10, 2)
);
INSERT INTO t_act(actno, balance) VALUES ('act001', 50000);
INSERT INTO t_act(actno, balance) VALUES ('act002', 0);
web.xml文件的版本较低,可以从tomcat10的样例文件中复制,然后修改
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="true">
web-app>
删除index.jsp文件,因为我们这个项目不使用JSP。只使用html。
引入相关依赖
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>17maven.compiler.source>
<maven.compiler.target>17maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.30version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.11version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
dependency>
dependencies>
IDEA配置Tomcat,这里Tomcat使用10+版本。并部署应用到tomcat。
引入相关配置文件,放到resources目录下(全部放到类的根路径下)
mybatis-config.xml
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="AccountMapper.xml"/>
mappers>
configuration>
AccountMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="account">
mapper>
logback.xml
<configuration debug="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
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}.logFileNamePattern>
<MaxHistory>30MaxHistory>
rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
encoder>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100MBMaxFileSize>
triggeringPolicy>
appender>
<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"/>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
root>
configuration>
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=root
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>
com.powernode.bank.pojo
com.powernode.bank.service
com.powernode.bank.service.impl
com.powernode.bank.dao
com.powernode.bank.dao.impl
com.powernode.bank.web.controller
com.powernode.bank.exception
com.powernode.bank.utils:将之前编写的SqlSessionUtil工具类拷贝到该包下。
public class SqlSessionUtil {
private SqlSessionUtil(){}
private static SqlSessionFactory sqlSessionFactory;
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 获取会话对象。
* @return 会话对象
*/
public static SqlSession openSession(){
return sqlSessionFactory.openSession();
}
}
public class Account {
private Long id;
private String actno;
private Double balance;
@Override
public String toString() {
return "Account{" +
"id=" + id +
", actno='" + actno + '\'' +
", balance=" + balance +
'}';
}
public Account() {
}
public Account(Long id, String actno, Double balance) {
this.id = id;
this.actno = actno;
this.balance = balance;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
}
@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) {
response.sendRedirect(request.getContextPath() + "/error1.html");
} catch (TransferException e) {
response.sendRedirect(request.getContextPath() + "/error2.html");
} catch (Exception e) {
response.sendRedirect(request.getContextPath() + "/error2.html");
}
}
}
/**
* 注意:业务类当中的业务方法的名字在起名的时候,最好见名知意,能够体现出具体的业务是做什么的。
* 账户业务类
*/
public interface AccountService {
/**
* 转账业务
* @param fromActno 转出账户
* @param toActno 转入账户
* @param money 转账金额
*/
void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException;
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = new AccountDaoImpl();
@Override
public void transfer(String fromActno, String toActno, double money)
throws MoneyNotEnoughException, TransferException {
// 1. 判断转出账户的余额是否充足(select)
Account fromAct = accountDao.selectByActno(fromActno);
// 2. 如果转出账户余额不足,提示用户
if (fromAct.getBalance() < money) { // 转出账户余额不足
throw new MoneyNotEnoughException("余额不足");
}
// 3. 如果转出账户余额充足,更新转出账户余额(update)
// 先更新java内存中对象的余额再更新数据库
Account toAct = accountDao.selectByActno(toActno);
fromAct.setBalance(fromAct.getBalance() - money);
toAct.setBalance(toAct.getBalance() + money);
int count = accountDao.updateByAccount(fromAct);
// 4. 更新转入账户余额(update)
count += accountDao.updateByAccount(toAct);
if (count != 2) {
throw new TransferException("转账失败");
}
}
}
/**
* 账户的DAO对象。负责t_act表中数据的CRUD.
* 强调一下:DAO对象中的任何一个方法和业务不挂钩。没有任何业务逻辑在里面。
* DAO中的方法就是做CRUD的。所以方法名大部分是:insertXXX deleteXXX updateXXX selectXXX
*/
public interface AccountDao {
/**
* 根据账号查询账户信息。
* @param actno 账号
* @return 账户信息
*/
Account selectByActno(String actno);
/**
* 更新账户信息
* @param act 被更新的账户对象
* @return 1表示更新成功,其他值表示失败。
*/
int updateByAccount(Account act);
}
public class AccountDaoImpl implements AccountDao {
@Override
public Account selectByActno(String actno) {
SqlSession sqlSession = SqlSessionUtil.openSession();
Account account = (Account) sqlSession.selectOne("account.selectByActno", actno);
sqlSession.close();
return account;
}
@Override
public int updateByAccount(Account act) {
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.update("account.updateByAccount", act);
sqlSession.commit();
sqlSession.close();
return count;
}
}
public class MoneyNotEnoughException extends Exception{
public MoneyNotEnoughException() {
}
public MoneyNotEnoughException(String message) {
super(message);
}
}
public class TransferException extends Exception{
public TransferException() {
}
public TransferException(String message) {
super(message);
}
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转账报告title>
head>
<body>
<h1>转账成功!h1>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转账报告title>
head>
<body>
<h1>余额不足!!!h1>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转账报告title>
head>
<body>
<h1>转账失败,未知原因!!!h1>
body>
html>
public class SqlSessionUtil {
private SqlSessionUtil(){}
private static SqlSessionFactory sqlSessionFactory;
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 全局的,服务器级别的,一个服务器当中定义一个即可。
// 为什么把SqlSession对象放到ThreadLocal当中呢?为了保证一个线程对应一个SqlSession。
private static ThreadLocal<SqlSession> local = new ThreadLocal<>();
/**
* 获取会话对象。
* @return 会话对象
*/
public static SqlSession openSession(){
SqlSession sqlSession = local.get();
if (sqlSession == null) {
sqlSession = sqlSessionFactory.openSession();
// 将sqlSession对象绑定到当前线程上。
local.set(sqlSession);
}
return sqlSession;
}
/**
* 关闭SqlSession对象(从当前线程中移除SqlSession对象。)
* @param sqlSession
*/
public static void close(SqlSession sqlSession) {
if (sqlSession != null) {
sqlSession.close();
// 注意移除SqlSession对象和当前线程的绑定关系。
// 因为Tomcat服务器支持线程池。也就是说:用过的线程对象t1,可能下一次还会使用这个t1线程。
local.remove();
}
}
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = new AccountDaoImpl();
@Override
public void transfer(String fromActno, String toActno, double money)
throws MoneyNotEnoughException, TransferException {
// 添加事务控制代码
SqlSession sqlSession = SqlSessionUtil.openSession();
// 1. 判断转出账户的余额是否充足(select)
Account fromAct = accountDao.selectByActno(fromActno);
// 2. 如果转出账户余额不足,提示用户
if (fromAct.getBalance() < money) { // 转出账户余额不足
throw new MoneyNotEnoughException("余额不足");
}
// 3. 如果转出账户余额充足,更新转出账户余额(update)
// 先更新java内存中对象的余额再更新数据库
Account toAct = accountDao.selectByActno(toActno);
fromAct.setBalance(fromAct.getBalance() - money);
toAct.setBalance(toAct.getBalance() + money);
int count = accountDao.updateByAccount(fromAct);
String s = null;
s.toUpperCase();
// 4. 更新转入账户余额(update)
count += accountDao.updateByAccount(toAct);
if (count != 2) {
throw new TransferException("转账失败");
}
// 提交事务
sqlSession.commit();
// 关闭事务
SqlSessionUtil.close(sqlSession);
}
}
public class AccountDaoImpl implements AccountDao {
@Override
public Account selectByActno(String actno) {
SqlSession sqlSession = SqlSessionUtil.openSession();
Account account = (Account) sqlSession.selectOne("account.selectByActno", actno);
// sqlSession.close();
return account;
}
@Override
public int updateByAccount(Account act) {
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.update("account.updateByAccount", act);
// sqlSession.commit();
// sqlSession.close();
return count;
}
}
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}
来自百度百科:
Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
要使用javassist,首先要引入它的依赖
<dependency>
<groupId>org.javassistgroupId>
<artifactId>javassistartifactId>
<version>3.29.1-GAversion>
dependency>
样例代码:
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import java.lang.reflect.Method;
public class JavassistTest {
public static void main(String[] args) throws Exception {
// 获取类池
ClassPool pool = ClassPool.getDefault();
// 创建类
CtClass ctClass = pool.makeClass("com.powernode.javassist.Test");
// 创建方法
// 1.返回值类型 2.方法名 3.形式参数列表 4.所属类
CtMethod ctMethod = new CtMethod(CtClass.voidType, "execute", new CtClass[]{}, ctClass);
// 设置方法的修饰符列表
ctMethod.setModifiers(Modifier.PUBLIC);
// 设置方法体
ctMethod.setBody("{System.out.println(\"hello world\");}");
// 给类添加方法
ctClass.addMethod(ctMethod);
// 调用方法
Class<?> aClass = ctClass.toClass();
Object o = aClass.newInstance();
Method method = aClass.getDeclaredMethod("execute");
method.invoke(o);
}
}
@Test
public void testGenerateFirstClass() throws Exception{
// 获取类池,这个类池就是用来给我生成class的
ClassPool pool = ClassPool.getDefault();
// 制造类(需要告诉javassist,类名是啥)
CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
// 制造方法
String methodCode = "public void insert(){System.out.println(123);}";
// 第一个参数:方法
// 第二个参数:将来方法要放到哪个类中
CtMethod ctMethod = CtMethod.make(methodCode, ctClass);
// 将方法添加到类中
ctClass.addMethod(ctMethod);
// 在内存中生成class
ctClass.toClass();
// 类加载到JVM当中,返回AccountDaoImpl类的字节码
Class<?> clazz = Class.forName("com.powernode.bank.dao.impl.AccountDaoImpl");
// 创建对象
Object obj = clazz.newInstance();
// 获取AccountDaoImpl中的insert方法
Method insertMethod = clazz.getDeclaredMethod("insert");
// 调用方法insert
insertMethod.invoke(obj);
}
@Test
public void testGenerateImpl() throws Exception{
// 获取类池
ClassPool pool = ClassPool.getDefault();
// 制造类
CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
// 制造接口
CtClass ctInterface = pool.makeInterface("com.powernode.bank.dao.AccountDao");
// 添加接口到类中,类实现接口
ctClass.addInterface(ctInterface); // AccountDaoImpl implements AccountDao
// 实现接口中的方法
// 制造方法
CtMethod ctMethod = CtMethod.make("public void delete(){System.out.println(\"hello delete!\");}", ctClass);
// 将方法添加到类中
ctClass.addMethod(ctMethod);
// 在内存中生成类,同时将生成的类加载到JVM当中。
Class<?> clazz = ctClass.toClass();
AccountDao accountDao = (AccountDao)clazz.newInstance();
accountDao.delete();
}
@Test
public void testGenerateAccountDaoImpl() throws Exception{
// 获取类池
ClassPool pool = ClassPool.getDefault();
// 制造类
CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
// 制造接口
CtClass ctInterface = pool.makeInterface("com.powernode.bank.dao.AccountDao");
// 实现接口
ctClass.addInterface(ctInterface);
// 实现接口中所有的方法
// 获取接口中所有的方法
Method[] methods = AccountDao.class.getDeclaredMethods();
Arrays.stream(methods).forEach(method -> {
// method是接口中的抽象方法
// 把method抽象方法给实现了。
try {
// public void delete(){}
// public int update(String actno, Double balance){}
StringBuilder methodCode = new StringBuilder();
methodCode.append("public "); // 追加修饰符列表
methodCode.append(method.getReturnType().getName()); // 追加返回值类型
methodCode.append(" ");
methodCode.append(method.getName()); //追加方法名
methodCode.append("(");
// 拼接参数 String actno, Double balance
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
methodCode.append(parameterType.getName());
methodCode.append(" ");
methodCode.append("arg" + i);
if(i != parameterTypes.length - 1){
methodCode.append(",");
}
}
methodCode.append("){System.out.println(11111); ");
// 动态的添加return语句
// 返回类型的简单名字
String returnTypeSimpleName = method.getReturnType().getSimpleName();
if ("void".equals(returnTypeSimpleName)) {
}else if("int".equals(returnTypeSimpleName)){
methodCode.append("return 1;");
}else if("String".equals(returnTypeSimpleName)){
methodCode.append("return \"hello\";");
}
methodCode.append("}");
System.out.println(methodCode);
CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
ctClass.addMethod(ctMethod); // 方法添加到类中
} catch (Exception e) {
e.printStackTrace();
}
});
// 在内存中生成class,并且加载到JVM当中
Class<?> clazz = ctClass.toClass();
// 创建对象
AccountDao accountDao = (AccountDao) clazz.newInstance();
// 调用方法
accountDao.insert("aaaaa");
accountDao.delete();
accountDao.update("aaaa", 1000.0);
accountDao.selectByActno("aaaa");
}
package com.powernode.bank.utils;
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;
/**
* 工具类:可以动态的生成DAO的实现类。(或者说可以动态生成DAO的代理类)
* 注意注意注意注意注意!!!!!!:
* 凡是使用GenerateDaoProxy的,SQLMapper.xml映射文件中namespace必须是dao接口的全名,id必须是dao接口中的方法名。
* @author 动力节点
* @version 1.0
* @since 1.0
*/
public class GenerateDaoProxy { // GenerateDaoProxy是mybatis框架的开发者写的。
/**
* 生成dao接口实现类,并且将实现类的对象创建出来并返回。
* @param daoInterface dao接口
* @return dao接口实现类的实例化对象。
*/
public static Object generate(SqlSession sqlSession, Class daoInterface){
// 类池
ClassPool pool = ClassPool.getDefault();
// 制造类(com.powernode.bank.dao.AccountDao --> com.powernode.bank.dao.AccountDaoProxy)
CtClass ctClass = pool.makeClass(daoInterface.getName() + "Proxy"); // 实际本质上就是在内存中动态生成一个代理类。
// 制造接口
CtClass ctInterface = pool.makeInterface(daoInterface.getName());
// 实现接口
ctClass.addInterface(ctInterface);
// 实现接口中所有的方法
Method[] methods = daoInterface.getDeclaredMethods();
Arrays.stream(methods).forEach(method -> {
// method是接口中的抽象方法
// 将method这个抽象方法进行实现
try {
// Account selectByActno(String actno);
// public Account selectByActno(String actno){ 代码; }
StringBuilder methodCode = new StringBuilder();
methodCode.append("public ");
methodCode.append(method.getReturnType().getName());
methodCode.append(" ");
methodCode.append(method.getName());
methodCode.append("(");
// 需要方法的形式参数列表
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
methodCode.append(parameterType.getName());
methodCode.append(" ");
methodCode.append("arg" + i);
if(i != parameterTypes.length - 1){
methodCode.append(",");
}
}
methodCode.append(")");
methodCode.append("{");
// 需要方法体当中的代码
// 对于javassist变量的类型需要使用全限定包名
methodCode.append("org.apache.ibatis.session.SqlSession sqlSession = com.powernode.bank.utils.SqlSessionUtil.openSession();");
// 需要知道是什么类型的sql语句
// sql语句的id是框架使用者提供的,具有多变性。对于我框架的开发人员来说。我不知道。
// 既然我框架开发者不知道sqlId,怎么办呢?mybatis框架的开发者于是就出台了一个规定:凡是使用GenerateDaoProxy机制的。
// sqlId都不能随便写。namespace必须是dao接口的全限定名称。id必须是dao接口中方法名。
String sqlId = daoInterface.getName() + "." + method.getName();
// getConfiguration() 获取mybatis核心配置
// getMappedStatement() 获取sql
// getSqlCommandType() 获取sql的类型 CRUD
// 获取sql语句的类型
SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
if (sqlCommandType == SqlCommandType.INSERT) {
}
if (sqlCommandType == SqlCommandType.DELETE) {
}
if (sqlCommandType == SqlCommandType.UPDATE) {
methodCode.append("return sqlSession.update(\""+sqlId+"\", arg0);");
}
if (sqlCommandType == SqlCommandType.SELECT) {
String returnType = method.getReturnType().getName();
methodCode.append("return ("+returnType+")sqlSession.selectOne(\""+sqlId+"\", arg0);");
}
methodCode.append("}");
CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
ctClass.addMethod(ctMethod);
} catch (Exception e) {
e.printStackTrace();
}
});
// 创建对象
Object obj = null;
try {
Class<?> clazz = ctClass.toClass();
obj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.powernode.bank.dao.AccountDao">
<select id="selectByActno" resultType="com.powernode.bank.pojo.Account">
select * from t_act where actno = #{actno}
select>
<update id="updateByActno">
update t_act set balance = #{balance} where actno = #{actno}
update>
mapper>
package com.powernode.bank.service.impl;
import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.dao.impl.AccountDaoImpl;
import com.powernode.bank.exceptions.MoneyNotEnoughException;
import com.powernode.bank.exceptions.TransferException;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import com.powernode.bank.utils.GenerateDaoProxy;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
public class AccountServiceImpl implements AccountService {
//private AccountDao accountDao = new AccountDaoImpl();
// 这是咱们自己封装的。
// SqlSession对象用于获取sql生成与sql对应的方法(不然要读配置文件获取sql)
//private AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(SqlSessionUtil.openSession(), AccountDao.class);
// 在mybatis当中,mybatis提供了相关的机制。也可以动态为我们生成dao接口的实现类。(代理类:dao接口的代理)
// mybatis当中实际上采用了代理模式。在内存中生成dao接口的代理类,然后创建代理类的实例。
// 使用mybatis的这种代理机制的前提:SqlMapper.xml文件中namespace必须是dao接口的全限定名称,id必须是dao接口中的方法名。
// 通过SqlSession调用,不用额外传入SqlSession对象
// 怎么用?代码怎么写?AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
// getMapper 映射器
private AccountDao accountDao = SqlSessionUtil.openSession().getMapper(AccountDao.class);
@Override
public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {
// 添加事务控制代码
SqlSession sqlSession = SqlSessionUtil.openSession();
// 1. 判断转出账户的余额是否充足(select)
Account fromAct = accountDao.selectByActno(fromActno);
if (fromAct.getBalance() < money) {
// 2. 如果转出账户余额不足,提示用户
throw new MoneyNotEnoughException("对不起,余额不足!");
}
// 3. 如果转出账户余额充足,更新转出账户余额(update)
// 先更新内存中java对象account的余额
Account toAct = accountDao.selectByActno(toActno);
fromAct.setBalance(fromAct.getBalance() - money);
toAct.setBalance(toAct.getBalance() + money);
int count = accountDao.updateByActno(fromAct);
// 模拟异常
/*String s = null;
s.toString();*/
// 4. 更新转入账户余额(update)
count += accountDao.updateByActno(toAct);
if (count != 2) {
throw new TransferException("转账异常,未知原因");
}
// 提交事务
sqlSession.commit();
// 关闭事务
SqlSessionUtil.close(sqlSession);
}
}
SqlSessionUtil
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;
import java.io.IOException;
/**
* MyBatis工具类
* @author 动力节点
* @version 1.0
* @since 1.0
*/
public class SqlSessionUtil {
private SqlSessionUtil(){}
private static SqlSessionFactory sqlSessionFactory;
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 全局的,服务器级别的,一个服务器当中定义一个即可。
// 为什么把SqlSession对象放到ThreadLocal当中呢?为了保证一个线程对应一个SqlSession。
private static ThreadLocal<SqlSession> local = new ThreadLocal<>();
/**
* 获取会话对象。
* @return 会话对象
*/
public static SqlSession openSession(){
SqlSession sqlSession = local.get();
if (sqlSession == null) {
sqlSession = sqlSessionFactory.openSession();
// 将sqlSession对象绑定到当前线程上。
local.set(sqlSession);
}
return sqlSession;
}
/**
* 关闭SqlSession对象(从当前线程中移除SqlSession对象。)
* @param sqlSession
*/
public static void close(SqlSession sqlSession){
if (sqlSession != null) {
sqlSession.close();
// 注意移除SqlSession对象和当前线程的绑定关系。
// 因为Tomcat服务器支持线程池。也就是说:用过的线程对象t1,可能下一次还会使用这个t1线程。
local.remove();
}
}
}
Car
package com.powernode.mybatis.pojo;
/**
* 封装汽车相关信息的pojo类。普通的java类。
* @author 动力节点
* @version 1.0
* @since 1.0
*/
public class Car {
// 数据库表当中的字段应该和pojo类的属性一一对应。
// 建议使用包装类,这样可以防止null的问题。
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
@Override
public String toString() {
return "Car{" +
"id=" + id +
", carNum='" + carNum + '\'' +
", brand='" + brand + '\'' +
", guidePrice=" + guidePrice +
", produceTime='" + produceTime + '\'' +
", carType='" + carType + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCarNum() {
return carNum;
}
/*public String getXyz() {
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;
}
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 Car() {
}
}
CarMapper
package com.powernode.mybatis.mapper; // 包名也有叫做:com.powernode.mybatis.dao
//mapper包就是dao包。
import com.powernode.mybatis.pojo.Car;
import java.util.List;
// 一般使用mybatis的话,一般不叫做XXXDao了。一般都是XXXMapper。
public interface CarMapper { //CarDao。
/**
* 新增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);
/**
* 获取所有的汽车信息。
* @return
*/
List<Car> selectAll();
}
CarMapper.xml
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 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="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>
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.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class CarMapperTest {
@Test
public void testInsert(){
SqlSession sqlSession = SqlSessionUtil.openSession();
// 面向接口获取接口的代理对象
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = new Car(null, "4444", "奔驰C200", 32.0, "2000-10-10", "新能源");
int count = mapper.insert(car);
System.out.println(count);
sqlSession.commit();
}
@Test
public void testDeleteById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
int count = mapper.deleteById(154L);
System.out.println(count);
sqlSession.commit();
}
@Test
public void testUpdate(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = new Car(155L, "2222", "凯美瑞222", 3.0, "2000-10-10", "新能源");
mapper.update(car);
sqlSession.commit();
}
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = mapper.selectById(155L);
System.out.println(car);
}
@Test
public void testSelectAll(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAll();
cars.forEach(car -> System.out.println(car));
}
}
#{}的执行结果:
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==>
Preparing:
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 = ?
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==>
Parameters: 新能源(String)
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - <==
Total: 2
${}的执行结果:
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==>
Preparing:
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 = 新能源
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==>
Parameters:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column '新能源' in 'where clause'
### The error may exist in CarMapper.xml
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: 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 = 新能源
### Cause: java.sql.SQLSyntaxErrorException: Unknown column '新能源' in 'where clause'
对查询结果进行升序或降序排序:
public interface CarMapper {
/**
* 查询所有的汽车信息。然后通过asc升序,desc降序。
* @param ascOrDesc
* @return
*/
List<Car> selectAllByAscOrDesc(String ascOrDesc);
}
<select id="selectAllByAscOrDesc" resultType="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>
@Test
public void testSelectAllByAscOrDesc(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAllByAscOrDesc("desc");
cars.forEach(car -> System.out.println(car));
sqlSession.close();
}
#{}的执行结果:
Preparing:
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 ?
Parameters: asc(String)
// 填充后的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 produce_time 'asc'
${}的执行结果:
Preparing:
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 asc
Parameters:
<mapper namespace="com.powernode.mybatis.mapper.LogMapper">
<select id="selectAllByTable" resultType="Log">
select * from t_log_${date}
select>
mapper>
public class LogMapperTest {
@Test
public void testSelectAllByTable(){
SqlSession sqlSession = SqlSessionUtil.openSession();
LogMapper mapper = sqlSession.getMapper(LogMapper.class);
List<Log> logs = mapper.selectAllByTable("20220901");
logs.forEach(log -> System.out.println(log));
}
}
批量删除的SQL语句有两种写法:
第一种or:delete from t_car where id=1 or id=2 or id=3;
第二种int:delete from t_car where id in(1,2,3);
应该采用${}
的方式:
delete from t_car where id in(${ids});
public interface CarMapper {
/**
* 批量删除,根据id
* @param ids
* @return
*/
int deleteBatch(String ids);
}
<delete id="deleteBatch">
delete from t_car where id in(${ids})
delete>
public void testDeleteBatch(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
int count = mapper.deleteBatch("155,156,157");
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
'%${brand}%'
concat('%',#{brand},'%')
concat('%','${brand}','%')
"%"#{brand}"%"
<select id="selectByBrandLike" resultType="CAR">
select
id,
car_num as carNum,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from
t_car
where
<!--brand like '%#{brand}%' 先进行编译 brand like '%?%' 里面的?被认为字符串的一部分无法进行数据的填充-->
<!--brand like '%${brand}%'-->
<!--brand like concat('%',#{brand},'%')-->
<!--brand like concat('%','${brand}','%')-->
brand like "%"#{brand}"%"
</select>
<typeAliases>
<typeAlias type="com.powernode.mybatis.pojo.Car"/>
<typeAlias type="com.powernode.mybatis.pojo.Log"/>
typeAliases>
<select id="selectByBrandLike" resultType="CAR">
<select id="selectAllByAscOrDesc" resultType="car">
<select id="selectByCarType" resultType="cAr">
<typeAliases>
<package name="com.powernode.mybatis.pojo"/>
typeAliases>
要求类的根路径下必须有:CarMapper.xml
要求在d:/下有CarMapper.xml文件
<mappers>
<package name="com.powernode.mybatis.mapper"/>
mappers>
mybatis-config.xml和SqlMapper.xml文件可以在IDEA中提前创建好模板,以后通过模板创建配置文件。
mybatis-config.xml 模板
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource=""/>
<typeAliases>
<package name=""/>
typeAliases>
<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>
<package name=""/>
mappers>
configuration>
SqlMapper.xml 模板
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
mapper>
前提是:主键是自动生成的。
插入一条新的记录之后,自动生成了主键,而这个主键需要在其他表中使用时。
第一种方式:可以先插入用户数据,再写一条查询语句获取id,然后再插入user_id字段。【比较麻烦】
第二种方式:mybatis提供了一种方式更加便捷。
<insert id="insertCarUseGeneratedKeys" useGeneratedKeys="true" keyProperty="id">
insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
insert>
@Test
public void testInsertCarUseGeneratedKeys(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = new Car(null,"9991", "凯美瑞", 30.0, "2020-11-11", "燃油车");
mapper.insertCarUseGeneratedKeys(car);
System.out.println(car);
sqlSession.commit();
sqlSession.close();
}
表:t_student
USE dbtest;
DROP TABLE IF EXISTS t_student;
CREATE TABLE t_student (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255),
age INT,
height DOUBLE,
birth DATE,
sex CHAR(1)
);
INSERT INTO t_student(name, age, height, birth, sex) VALUES ('张三', 20, 1.77, '2001-01-02', '男');
INSERT INTO t_student(name, age, height, birth, sex) VALUES ('李四', 20, 1.67, '2001-11-22', '女');
pojo类:
package com.powernode.mybatis.pojo;
import java.util.Date;
/**
* 学生类
* @author 老杜
* @version 1.0
* @since 1.0
*/
public class Student {
private Long id;
private String name;
private Integer age;
private Double height;
private Character sex;
private Date birth;
// constructor
// setter and getter
// toString
}
<select id="selectById" resultType="Student" parameterType="long">
select * from t_student where id = #{id}
select>
<select id="selectByName" resultType="student">
select * from t_student where name = #{name, javaType=String, jdbcType=VARCHAR}
select>
<select id="selectByBirth" resultType="student">
select * from t_student where birth = #{birth}
select>
<select id="selectBySex" resultType="student">
select * from t_student where sex = #{sex}
select>
【开发手册 – 内置别名】
<insert id="insertStudentByMap">
insert into t_student(id,name,age,sex,birth,height) values(null,#{姓名},#{年龄},#{性别},#{生日},#{身高})
insert>
<!--
保存学生信息,通过POJO参数。Student是单个参数。但是不是简单类型。
int insertStudentByPOJO(Student student);
-->
<!-- 这里需要注意的是:#{} 里面写的是属性名字。这个属性名其本质上是:set/get方法名去掉set/get之后的名字。-->
<!--<insert id="insertStudentByPOJO" parameterType="student">-->
<!-- 可以自动推出类型为Student类 -->
<insert id="insertStudentByPOJO">
insert into t_student(id,name,age,sex,birth,height) values(null,#{name},#{age},#{sex},#{birth},#{height})
</insert>
<select id="selectByNameAndSex" resultType="Student">
select * from t_student where name = #{arg0} and sex = #{param2}
select>
<select id="selectByNameAndSex2" resultType="Student">
select * from t_student where name = #{name} and sex = #{sex}
select>
<select id="selectByIdRetMap" resultType="map">
select * from t_car where id = #{id}
select>
/**
* 根据id获取汽车信息。将汽车信息放到Map集合中。
* +-----+---------+----------+-------------+--------------+----------+
* | id | car_num | brand | guide_price | produce_time | car_type |
* +-----+---------+----------+-------------+--------------+----------+
* | 158 | 1111 | 比亚迪汉 | 3.00 | 2000-10-10 | 新能源 |
* +-----+---------+----------+-------------+--------------+----------+
*
* Map
* k v
* -----------------------
* "id" 158
* "car_num" 1111
* "brand" 比亚迪汉
* ....
*
* @param id
* @return
*/
Map<String, Object> selectByIdRetMap(Long id);
List
查询结果条数大于等于1条数据,则可以返回一个存储Map集合的List集合。List
等同于List
/**
* 查询所有的Car信息。返回一个存放Map集合的List集合。
* @return
*/
List<Map<String,Object>> selectAllRetListMap();
<select id="selectAllRetListMap" resultType="map">
select * from t_car
select>
/**
* 查询所有的Car,返回一个大Map集合。
* Map集合的key是每条记录的主键值。
* Map集合的value是每条记录。
* {
* 160={car_num=3333, id=160, guide_price=32.00, produce_time=2000-10-10, brand=奔驰E300L, car_type=新能源},
* 161={car_num=4444, id=161, guide_price=32.00, produce_time=2000-10-10, brand=奔驰C200, car_type=新能源},
* 162={car_num=9999, id=162, guide_price=30.00, produce_time=2020-10-11, brand=帕萨特, car_type=燃油车},
* 163={car_num=9991, id=163, guide_price=30.00, produce_time=2020-11-11, brand=凯美瑞, car_type=燃油车},
* 158={car_num=1111, id=158, guide_price=3.00, produce_time=2000-10-10, brand=比亚迪汉, car_type=新能源},
* 159={car_num=2222, id=159, guide_price=32.00, produce_time=2000-10-10, brand=比亚迪秦, car_type=新能源}
* }
* @return
*/
@MapKey("id") // 将查询结果的id值作为整个大Map集合的key。
Map<Long, Map<String,Object>> selectAllRetMap();
<select id="selectAllRetMap" resultType="map">
select * from t_car
select>
/**
* 查询所有的Car信息。使用resultMap标签进行结果映射。
* @return
*/
List<Car> selectAllByResultMap();
<resultMap id="carResultMap" type="Car">
<id property="id" column="id"/>
<result property="carNum" column="car_num" javaType="java.lang.String" jdbcType="VARCHAR"/>
<result property="guidePrice" column="guide_price"/>
<result property="produceTime" column="produce_time"/>
<result property="carType" column="car_type" javaType="string" jdbcType="VARCHAR"/>
resultMap>
<select id="selectAllByResultMap" resultMap="carResultMap">
select * from t_car
select>
在mybatis核心配置文件中进行配置
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
/**
* 查询所有的Car信息。但是启用了驼峰命名自动映射机制。
* @return
*/
List<Car> selectAllByMapUnderscoreToCamelCase();
<select id="selectAllByMapUnderscoreToCamelCase" resultType="Car">
select * from t_car
select>
/**
* 获取Car的总记录条数。
* @return
*/
Long selectTotal();
<select id="selectTotal" resultType="_long">
select count(*) from t_car
select>
delete from t_car where id in(1,2,3,4,5,6,......这里的值是动态的,根据用户选择的id不同,值是不同的);
select * from t_car where brand like '丰田%' and guide_price > 30 and .....;
/**
* 多条件查询
* @param brand 品牌
* @param guidePrice 指导价
* @param carType 汽车类型
* @return
*/
List<Car> selectByMultiCondition(@Param("brand") String brand, @Param("guidePrice") Double guidePrice, @Param("carType") String carType);
<select id="selectByMultiCondition" resultType="Car">
select * from t_car where 1 = 1
<if test="brand != null and brand != ''">
and brand like "%"#{brand}"%"
if>
<if test="guidePrice != null and guidePrice != ''">
and guide_price > #{guidePrice}
if>
<if test="carType != null and carType != ''">
and car_type = #{carType}
if>
select>
@Test
public void testSelectByMultiCondition(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 假设三个条件都不是空
//List cars = mapper.selectByMultiCondition("比亚迪", 2.0, "新能源");
// 假设三个条件都是空
//List cars = mapper.selectByMultiCondition("", null, "");
// 假设后两个条件不为空,第一个条件为空
//List cars = mapper.selectByMultiCondition("", 2.0, "新能源");
// 假设第一个条件不是空,后两个条件是空
List<Car> cars = mapper.selectByMultiCondition("比亚迪", null, "");
cars.forEach(car -> System.out.println(car));
sqlSession.close();
}
/**
* 使用where标签,让where子句更加的智能。
* @param brand
* @param guidePrice
* @param carType
* @return
*/
List<Car> selectByMultiConditionWithWhere(@Param("brand") String brand, @Param("guidePrice") Double guidePrice, @Param("carType") String carType);
<select id="selectByMultiConditionWithWhere" resultType="Car">
select * from t_car
<where>
<if test="brand != null and brand != ''">
and brand like "%"#{brand}"%"
if>
<if test="guidePrice != null and guidePrice != ''">
and guide_price > #{guidePrice}
if>
<if test="carType != null and carType != ''">
and car_type = #{carType}
if>
where>
select>
@Test
public void testSelectByMultiConditionWithWhere(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 三个条件都不是空
//List cars = mapper.selectByMultiConditionWithWhere("比亚迪", 2.0, "新能源");
// 三个条件都是空
//List cars = mapper.selectByMultiConditionWithWhere("", null, "");
// 如果第一个条件是空
//List cars = mapper.selectByMultiConditionWithWhere("", 2.0, "新能源");
// 后面两个条件是空
List<Car> cars = mapper.selectByMultiConditionWithWhere("比亚迪", null, "");
cars.forEach(car -> System.out.println(car));
sqlSession.close();
}
<select id="selectByMultiConditionWithTrim" resultType="Car">
select * from t_car
<trim prefix="where" suffixOverrides="and|or">
<if test="brand != null and brand != ''">
brand like "%"#{brand}"%" or
if>
<if test="guidePrice != null and guidePrice != ''">
guide_price > #{guidePrice} and
if>
<if test="carType != null and carType != ''">
car_type = #{carType}
if>
trim>
select>
<update id="updateBySet">
update t_car
<set>
<if test="carNum != null and carNum != ''">car_num = #{carNum},if>
<if test="brand != null and brand != ''">brand = #{brand},if>
<if test="guidePrice != null and guidePrice != ''">guide_price = #{guidePrice},if>
<if test="produceTime != null and produceTime != ''">produce_time = #{produceTime},if>
<if test="carType != null and carType != ''">car_type = #{carType},if>
set>
where
id = #{id}
update>
这三个标签是必须在一起使用的:
<choose>
<when>when>
<when>when>
<when>when>
<otherwise>otherwise>
choose>
等同于:
if(){
}else if(){
}else if(){
}else if(){
}else{
}
只有一个分支会被选择!!!!
<select id="selectByChoose" resultType="Car">
select * from t_car
<where>
<choose>
<when test="brand != null and brand != ''">
brand like "%"#{brand}"%"
when>
<when test="guidePrice != null and guidePrice != ''">
guide_price > #{guidePrice}
when>
<otherwise>
car_type = #{carType}
otherwise>
choose>
where>
select>
循环数组或集合,动态生成sql,比如这样的SQL:
delete from t_car where id in(1,2,3);
delete from t_car where id = 1 or id = 2 or id = 3;
insert into t_car values
(null,'1001','凯美瑞',35.0,'2010-10-11','燃油车'),
(null,'1002','比亚迪唐',31.0,'2020-11-11','新能源'),
(null,'1003','比亚迪宋',32.0,'2020-10-11','新能源')
/**
* 批量删除。foreach标签
* @param ids
* @return
*/
int deleteByIds(@Param("ids") Long[] ids);
<delete id="deleteByIds">
delete from t_car where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
foreach>
delete>
/**
* 批量插入,一次插入多条Car信息
* @param cars
* @return
*/
int insertBatch(@Param("cars") List<Car> cars);
<insert id="insertBatch">
insert into t_car values
<foreach collection="cars" item="car" separator=",">
(null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
foreach>
insert>
/**
* 根据id批量删除 使用or关键字。
* @param ids
* @return
*/
int deleteByIds2(@Param("ids") Long[] ids);
<delete id="deleteByIds2">
delete from t_car where
<foreach collection="ids" item="id" separator="or">
id=#{id}
foreach>
delete>
<sql id="carCols">
id,
car_num carNum,
brand,
guide_price guidePrice,
produce_time produceTime,
car_type carType
sql>
<select id="selectAllRetMap" resultType="map">
select <include refid="carCols"/> from t_car
select>
<select id="selectAllRetListMap" resultType="map">
select <include refid="carCols"/> carType from t_car
select>
<select id="selectByIdRetMap" resultType="map">
select <include refid="carCols"/> from t_car where id = #{id}
select>
多对一的映射实体类设计:
/**
* 学生信息
*/
public class Student { // Student是多的一方
private Integer sid;
private String sname;
private Clazz clazz; // Clazz是一的一方。
......
}
/**
* 班级信息
*/
public class Clazz {
private Integer cid;
private String cname;
......
}
/**
* 根据id获取学生信息。同时获取学生关联的班级信息。
* @param id 学生的id
* @return 学生对象,但是学生对象当中含有班级对象。
*/
Student selectById(Integer id);
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<result property="clazz.cid" column="cid"/>
<result property="clazz.cname" column="cname"/>
resultMap>
<select id="selectById" resultMap="studentResultMap">
select
s.sid,s.sname,c.cid,c.cname
from
t_stu s left join t_clazz c on s.cid = c.cid
where
s.sid = #{sid}
select>
/**
* 一条SQL语句,association
* @param id
* @return
*/
Student selectByIdAssociation(Integer id);
<resultMap id="studentResultMapAssociation" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz" javaType="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
association>
resultMap>
<select id="selectByIdAssociation" resultMap="studentResultMapAssociation">
select
s.sid,s.sname,c.cid,c.cname
from
t_stu s left join t_clazz c on s.cid = c.cid
where
s.sid = #{sid}
select>
/**
* 分部查询第一步:先根据学生的sid查询学生的信息。
* @param sid
* @return
*/
Student selectByIdStep1(Integer sid);
<resultMap id="studentResultMapByStep" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz"
select="com.powernode.mybatis.mapper.ClazzMapper.selectByIdStep2"
column="cid"
fetchType="eager"/>
resultMap>
<select id="selectByIdStep1" resultMap="studentResultMapByStep">
select sid,sname,cid from t_stu where sid = #{sid}
select>
/**
* 分步查询第二步:根据cid获取班级信息。
* @param cid
* @return
*/
Clazz selectByIdStep2(Integer cid);
<select id="selectByIdStep2" resultType="Clazz">
select cid,cname from t_clazz where cid = #{cid}
select>
全局开启延迟加载:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
settings>
多对一的映射实体类设计:
一对多的实现,通常是在一的一方中有List集合属性。
在Clazz类中添加List
属性。
/**
* 班级信息
*/
public class Clazz {
private Integer cid;
private String cname;
private List<Student> stus;
......
}
/**
* 学生信息
*/
public class Student { // Student是多的一方
private Integer sid;
private String sname;
private Clazz clazz; // Clazz是一的一方。
......
}
/**
* 根据班级编号查询班级信息。
* @param cid
* @return
*/
Clazz selectByCollection(Integer cid);
<resultMap id="clazzResultMap" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<collection property="stus" ofType="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
collection>
resultMap>
<select id="selectByCollection" resultMap="clazzResultMap">
select c.cid,c.cname,s.sid,s.sname from t_clazz c left join t_stu s on c.cid = s.cid where c.cid = #{cid}
select>
/**
* 分步查询。第一步:根据班级编号获取班级信息。
* @param cid 班级编号
* @return
*/
Clazz selectByStep1(Integer cid);
<resultMap id="clazzResultMapStep" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<collection property="stus"
select="com.powernode.mybatis.mapper.StudentMapper.selectByCidStep2"
column="cid" fetchType="eager" />
resultMap>
<select id="selectByStep1" resultMap="clazzResultMapStep">
select cid,cname from t_clazz where cid = #{cid}
select>
/**
* 根据班级编号查询学生信息。
* @param cid
* @return
*/
List<Student> selectByCidStep2(Integer cid);
<select id="selectByCidStep2" resultType="Student">
select * from t_stu where cid = #{cid}
select>
sqlSession.clearCache();
全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认就是true,无需设置。
<cache/>
public class Car implements Serializable {}
@Test
public void testSelectById2() throws Exception{
// 这里只有一个SqlSessionFactory对象。二级缓存对应的就是SqlSessionFactory。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
// 这行代码执行结束之后,实际上数据是缓存到一级缓存当中了。(sqlSession1是一级缓存。)
Car car1 = mapper1.selectById2(164L);
System.out.println(car1);
// 如果这里不关闭SqlSession1对象的话,二级缓存中还是没有数据的。
// 如果执行了这行代码,sqlSession1的一级缓存中的数据会放到二级缓存当中。
sqlSession1.close();
// 这行代码执行结束之后,实际上数据会缓存到一级缓存当中。(sqlSession2是一级缓存。)
Car car2 = mapper2.selectById2(164L);
System.out.println(car2);
// 程序执行到这里的时候,会将sqlSession1这个一级缓存中的数据写入到二级缓存当中。
//sqlSession1.close();
// 程序执行到这里的时候,会将sqlSession2这个一级缓存中的数据写入到二级缓存当中。
sqlSession2.close();
}
【https://www.yuque.com/zuihoudewu/java_note/mt2812#IB2eW】
第一步:引入mybatis整合ehcache的依赖。
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.2.2version>
dependency>
第二步:在类的根路径下新建echcache.xml文件,并提供以下配置信息。
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="e:/ehcache"/>
<defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
ehcache>
第三步:修改SqlMapper.xml文件中的标签,添加type属性。
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
第四步:编写测试程序使用。
@Test
public void testSelectById2() throws Exception{
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
Car car1 = mapper1.selectById(83L);
System.out.println(car1);
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
Car car2 = mapper2.selectById(83L);
System.out.println(car2);
}
第一步:基础环境准备
新建模块:mybatis-011-generator
打包方式:jar
第二步:在pom中添加逆向工程插件(project标签下)
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>1.4.1version>
<configuration>
<overwrite>trueoverwrite>
configuration>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.30version>
dependency>
dependencies>
plugin>
plugins>
build>
第三步:配置generatorConfig.xml
该文件名必须叫做:generatorConfig.xml
该文件必须放在类的根路径下。
DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true"/>
commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/powernode"
userId="root"
password="root">
jdbcConnection>
<javaModelGenerator targetPackage="com.powernode.mybatis.pojo" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
javaModelGenerator>
<sqlMapGenerator targetPackage="com.powernode.mybatis.mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
sqlMapGenerator>
<javaClientGenerator
type="xmlMapper"
targetPackage="com.powernode.mybatis.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
javaClientGenerator>
<table tableName="t_car" domainObjectName="Car"/>
context>
generatorConfiguration>
第四步:运行插件
generatorConfig.xml配置文件中targetRuntime的值选择MyBatis3,生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。
<context id="DB2Tables" targetRuntime="MyBatis3">
生成的XxxxExample类负责封装查询条件
public class CarMapperTest {
// CarExample类负责封装查询条件的。
@Test
public void testSelect(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 执行查询
// 1. 查询一个
Car car = mapper.selectByPrimaryKey(165L);
System.out.println(car);
// 2. 查询所有(selectByExample,根据条件查询,如果条件是null表示没有条件。)
List<Car> cars = mapper.selectByExample(null);
cars.forEach(car1 -> System.out.println(car1));
System.out.println("=========================================");
// 3. 按照条件进行查询
// QBC 风格:Query By Criteria 一种查询方式,比较面向对象,看不到sql语句。
// 封装条件,通过CarExample对象来封装查询条件
CarExample carExample = new CarExample();
// 调用carExample.createCriteria()方法来创建查询条件
// 继续向后调用方法追加查询条件
carExample.createCriteria()
.andBrandLike("帕萨特")
.andGuidePriceGreaterThan(new BigDecimal(20.0));
// 添加or
// 上面添加的为and条件,or()开始添加or条件
// (... and ... 前面的查询条件) or (...) 生成后的查询条件
carExample.or().andCarTypeEqualTo("燃油车");
// 执行查询
List<Car> cars2 = mapper.selectByExample(carExample);
cars2.forEach(car2 -> System.out.println(car2));
sqlSession.close();
}
}
所以,标准通用的mysql分页SQL:
select
*
from
tableName ......
limit
(pageNum - 1) * pageSize, pageSize
使用mybatis应该怎么做?
/**
* 分页查询
* @param startIndex 起始下标。
* @param pageSize 每页显示的记录条数
* @return
*/
List<Car> selectByPage(@Param("startIndex") int startIndex, @Param("pageSize") int pageSize);
<select id="selectByPage" resultType="Car">
select * from t_car limit #{startIndex},#{pageSize}
select>
@Test
public void testSelectByPage(){
// 获取每页显示的记录条数
int pageSize = 3;
// 显示第几页:页码
int pageNum = 3;
// 计算开始下标
int startIndex = (pageNum - 1) * pageSize;
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectByPage(startIndex, pageSize);
cars.forEach(car -> System.out.println(car));
sqlSession.close();
}
获取数据不难,难的是获取分页相关的数据比较难(是否还有下一页等)。可以借助mybatis的PageHelper插件。
使用PageHelper插件进行分页,更加的便捷。
第一步:引入依赖
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.3.1version>
dependency>
第二步:在mybatis-config.xml文件中配置插件
typeAliases标签下面进行配置:
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">plugin>
plugins>
第三步:编写Java代码
/**
* 查询所有的Car,通过分页查询插件PageHelper完成。
* @return
*/
List<Car> selectAll();
<select id="selectAll" resultType="Car">
select * from t_car
select>
@Test
public void testSelectAll(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 一定一定一定要注意:在执行DQL语句之前。开启分页功能。
int pageNum = 2;
int pageSize = 3;
PageHelper.startPage(pageNum, pageSize);
List<Car> cars = mapper.selectAll();
//cars.forEach(car -> System.out.println(car));
// 查询完之后创建PageInfo对象获取分页的相关信息
// 封装分页信息对象new PageInfo()
// PageInfo对象是PageHelper插件提供的,用来封装分页相关的信息的对象。
// 第一个参数为查询出来的数据,第二个参数为导航的页数(导航有几页)
// navigatePages=3, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]
PageInfo<Car> carPageInfo = new PageInfo<>(cars, 3);
System.out.println(carPageInfo);
sqlSession.close();
/*
PageInfo{pageNum=2, pageSize=3, size=3, startRow=4, endRow=6, total=7, pages=3,
list=Page{count=true, pageNum=2, pageSize=3, startRow=3, endRow=6, total=7, pages=3, reasonable=false, pageSizeZero=false}
[Car{id=168, carNum='1204', brand='奥迪Q7', guidePrice=3.0, produceTime='2009-10-11', carType='燃油车'},
Car{id=169, carNum='1205', brand='朗逸', guidePrice=4.0, produceTime='2001-10-11', carType='新能源'},
Car{id=170, carNum='1206', brand='奔驰E300L', guidePrice=50.0, produceTime='2003-02-03', carType='新能源'}],
prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true,
navigatePages=3, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]}
*/
}
@Insert("insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})")
int insert(Car car);
@Delete("delete from t_car where id = #{id}")
int deleteById(Long id);
@Update("update t_car set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType} where id=#{id}")
int update(Car car);
@Select("select * from t_car where id = #{id}")
Car selectById(Long id);
@Select("select * from t_car where id = #{id}")
// 定义属性名与字段名的对应关系
@Results({
@Result(property = "id", column = "id"),
@Result(property = "carNum", column = "car_num"),
@Result(property = "brand", column = "brand"),
@Result(property = "guidePrice", column = "guide_price"),
@Result(property = "produceTime", column = "produce_time"),
@Result(property = "carType", column = "car_type")
})
Car selectById(Long id);