赞
踩
文章修改记录:
2023-02-01 第一版:https://www.yuque.com/u27599042/un32ge/mfpui3w5mgiugbhp
2023-06-04 完成重构 第二版:https://www.yuque.com/u27599042/un32ge/kggx3zvyg7mztgvd
手写 MyBatis 框架[GodBatis]第五章节单独抽取了出来:https://www.yuque.com/u27599042/un32ge/ru8czamo6trse3rl
文章汇总归纳于:https://www.yuque.com/u27599042/un32ge
老杜MyBatis原版笔记:https://www.yuque.com/zuihoudewu/java_note/mt2812
资料
1、javaWeb 链接:https://pan.baidu.com/s/1T3ouoZuZCMCAwPRv1Tahdg?pwd=5u25 提取码:5u25
2、主流框架 链接:https://pan.baidu.com/s/10HGe7wP1aed2HUCihc3-yQ?pwd=afjd 提取码:afjd
3、微服务架构 链接:https://pan.baidu.com/s/14RCkZXWyRP5hlOpDpp_ESQ?pwd=g0at 提取码:g0at
4、互联网生态 链接:https://pan.baidu.com/s/1IlM4LAU2gQqUMeN_B48t8w?pwd=egl7 提取码:egl7
6、架构师必会 链接:https://pan.baidu.com/s/10fPzIzSskuecnSxs4F4FRQ?pwd=s8tg 提取码:s8tg
MyBatis GitHub 网址:https://github.com/mybatis/mybatis-3
# 创建数据库 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', '新能源');
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eR8ThlDz-1685870936754)(https://www.yuque.com/api/filetransfer/images?url=https%3A%2F%2Fimg-blog.csdnimg.cn%2Fd860610f0184434e9077fb5027e9e597.png&sign=98ee78e42953c41fb0b78df7a3357393b05f83cceb62b7a4aa71fc680c7a3509#from=url&id=CPSPR&originHeight=179&originWidth=1135&originalType=binary&ratio=1.25&rotation=0&showTitle=false&status=done&style=none&title=)]
<groupId>cw</groupId>
<artifactId>mybatis-study-001</artifactId>
<version>1.0-SNAPSHOT</version>
<!--
设置打包方式为 jar,
MyBatis 是封装 JDBC 的框架
不创建web项目所以不需要使用war的打包方式
-->
<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>
</dependencies>
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- 修改链接数据库的配置信息 --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_study"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <!--sql映射⽂件创建好之后,需要将该⽂件路径配置到这⾥--> <mapper resource=""/> </mappers> </configuration>
<?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 sql:保存⼀个汽⻋信息-->
<!-- id 是这条SQL语句的唯一标识,这个id就代表了这条SQL语句 -->
<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>
<!-- sql映射⽂件创建好之后,需要将该⽂件路径配置到这⾥ -->
<!-- 和 resource 相关的路径配置,默认都是从类根路径开始 -->
<mapper resource="CarMapper.xml"/>
</mappers>
/** * ClassName: Main * Package: cw.mybatis.study.main * Description: * * @Author tcw * @Create 2023-05-25 23:55 * @Version 1.0 */ public class Main { public static void main(String[] args) throws IOException { // 获取SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取 SqlSessionFactory 对象 // 获取 SqlSessionFactory 对象,需要读取配置文件,根据配置文件中的信息创建 SqlSessionFactory 对象 // Resources.getResourceAsStream 默认就是从类的根路径下开始查找资源。 InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); // 以流的形式加载资源的方式二 //InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml"); // 需要传入核心配置文件对应的输入流,一般情况下都是一个数据库对应一个 SqlSessionFactory 对象。 // 根据配置文件中的信息创建 SqlSessionFactory 对象 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); // 通过 SqlSessionFactory 对象获取 SqlSession 对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 开启与数据库之间的会话 // 执行 SQL 语句 // 传入SQL语句对应的id,执行配置文件中id对应的SQL语句, // 返回值是影响数据库表当中的记录条数。 int count = sqlSession.insert("insertCar"); System.out.println("插入了几条记录:" + count); // 手动提交事务 // 注意:默认采用的事务管理器是:JDBC。 // JDBC事务默认是不提交的,需要手动提交。 sqlSession.commit(); // 关闭本次与数据库的会话 sqlSession.close(); } }
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");
<mapper resource="CarMapper.xml"/>
resource属性:这种方式是从类路径当中加载资源。<mapper url="file:///d:/CarMapper.xml"/>
url属性:这种方式是从绝对路径当中加载资源。(绝对路径前需要加上file:///
)<transactionManager type="JDBC"/>
conn.setAutoCommit(false); // 开启事务。
// ....业务处理...
conn.commit(); // 手动提交事务
// 获取SqlSession对象
// 如果使用的事务管理器是JDBC的话,底层实际上会执行:conn.setAutoCommit(false);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行SQL语句
// 返回值是影响数据库表当中的记录条数。
int count = sqlSession.insert("insertCar");
System.out.println("插入了几条记录:" + count);
// 手动提交
// 如果使用的事务管理器是JDBC的话,底层实际上还是会执行conn.commit();
sqlSession.commit();
SqlSession sqlSession = sqlSessionFactory.openSession(true);
,表示没有开启事务。
conn.setAutoCommit(false);
conn.setAutoCommit(false);
那么autoCommit就是true。如果autoCommit是true,就表示没有开启事务(事务自动提交)。只要执行任意一条DML语句就提交一次。conn.setAutoCommit(false);
的话,默认的autoCommit是true。package cw.mybatis.study.main; 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; import java.io.InputStream; /** * ClassName: Main * Package: cw.mybatis.study.main * Description: * * @Author tcw * @Create 2023-05-25 23:55 * @Version 1.0 */ public class Main { public static void main(String[] args){ SqlSession sqlSession = null; // 与数据库会话的对象 try { // 获取SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取 SqlSessionFactory 对象 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml")); // 通过 SqlSessionFactory 对象获取 SqlSession 对象 // 开启与数据库之间的会话 sqlSession = sqlSessionFactory.openSession(); // 执行 SQL 语句 int count = sqlSession.insert("insertCar"); System.out.println("插入了几条记录:" + count); // 到这里没有报错,说明执行成功,可以提交本次事务 // 手动提交事务 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); // 由异常说明事务执行过程中出现错误 // 回滚事务 if (sqlSession != null) sqlSession.rollback(); } finally { // 关闭本次与数据库的会话(释放资源) if (sqlSession != null) sqlSession.close(); } } }
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
@Test
注解进⾏标注要进行测试的类 + Test
test + 要测试方法的方法名
,public void testXxxx() {}
,每个测试方法需要使用@Test
注解进行标注,表示该方法是一个单元测试方法package com.powernode.junit.service; import org.junit.Assert; import org.junit.Test; // 测试类的类名:要进行测试的类 + Test public class MathServiceTest { @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依赖,logback日志框架实现了slf4j规范 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
<?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>[%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!--mybatis log configure--> <logger name="com.apache.ibatis" level="TRACE"/> <logger name="java.sql.Connection" level="DEBUG"/> <logger name="java.sql.Statement" level="DEBUG"/> <logger name="java.sql.PreparedStatement" level="DEBUG"/> <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR --> <!-- 级别越低输出的信息越多 --> <root level="DEBUG"> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> </root> </configuration>
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对象。 // SqlSessionFactory对象:一个SqlSessionFactory对应一个environment,一个environment通常是一个数据库。 static { SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); 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(); } }
<!--namespace先随便写-->
<mapper namespace="car">
<!-- 在insert标签中写向数据库中插入数据的SQL -->
<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>
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);
....
<!--namespace先随便写-->
<mapper namespace="car">
<insert id="insertCar">
insert into t_car(car_num,brand,guide_price,produce_time,car_type)
values(#{}, #{}, #{}, #{}, #{})
</insert>
</mapper>
<mapper namespace="car">
<!--insert sql:保存⼀个汽⻋信息-->
<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 SQL 使用 insert方法 // insert方法的参数: // 第一个参数:sqlId,从CarMapper.xml文件中复制。 // 第二个参数:封装数据的对象。 sqlSession.insert("insertCar", map); sqlSession.commit(); sqlSession.close(); } }
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 + '\'' + '}'; } }
@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();
}
<mapper namespace="car">
<!--insert sql:保存⼀个汽⻋信息-->
<!-- 占位符 #{} 中写POJO的属性名 -->
<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})
There is no getter for property named 'xyz' in 'class com.powernode.mybatis.pojo.Car'
<insert id="insertCar" parameterType="java.util.Map">
insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>
<insert id="insertCarByPOJO" parameterType="com.powernode.mybatis.pojo.Car">
insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>
<!-- 当sql语句中的占位符只有一个时,占位符中的内容可以任意写,但是不能不写,一般采用见名知意的命名 -->
<!-- 只有一个占位符时,mybatis可以知道数据填放的位置 -->
<delete id="deleteById">
delete from t_car where id = #{id}
</delete>
@Test
public void testDeleteById() {
SqlSession sqlSession = SqlSessionUtils.openSession();
// 第二个参数会被自动装箱成相应的类型
sqlSession.delete("deleteById", 10);
sqlSession.commit();
sqlSession.close();
}
<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();
}
<!--
sql语句中如果表的字段名与相应的java类的属性名不一致需要使用别名为查询语句的字段指定别名,
否则对应字段的查询为null
-->
<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();
}
<!--
resultType还是指定要封装的结果集的类型。
不是指定List类型,是指定List集合中元素的类型。
-->
<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();
// selectList方法:mybatis通过这个方法就可以得知你需要一个List集合。
// 它会自动给你返回一个List集合。
List<Car> cars = sqlSession.selectList("selectAll");
cars.forEach(System.out::println);
sqlSession.close();
}
namespace.sqlId
(MyBatis中sqlId的完整写法)<?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="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>
<mappers>
<mapper resource="CarMapper.xml"/>
<mapper resource="CarMapper2.xml"/>
</mappers>
@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前添加一个名称空间,要有你改个其它名字。
@Test
public void testSelectAll() {
SqlSession sqlSession = SqlSessionUtils.openSession();
// SQL id:namespace.id
List<Car> cars = sqlSession.selectList("car.selectAll");
cars.forEach(System.out::println);
sqlSession.close();
}
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/powernode"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="CarMapper.xml"/> <mapper resource="CarMapper2.xml"/> </mappers> </configuration>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
...
</configuration>
${property的name属性}
取出相应的值<properties>
<!-- <property name="属性名" value="属性值"/> -->
<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/powernode"/>
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="root"/>
</properties>
<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>
<!-- resource,从类路径下开始查找资源 -->
<properties resource="jdbc.properties" />
<!-- 从绝对路径当中加载资源 -->
<!-- 绝对路径怎么写?file:///路径 -->
<properties url="file:///d:/jdbc.properties" />
<environments default="powernodeDB">
...
</environments>
<!-- 第一个数据库环境 --> <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>
SqlSessionFactory sqlSessionFactory =
sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSessionFactory sqlSessionFactory1 =
sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "powernodeDB");
<transactionManager type="JDBC"/>
conn.setAutoCommit(false);
...
conn.commit();
<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>
<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}"/> <!-- 以下配置数据库连接池的参数 --> <!--提醒:正常使用连接池的话,池中有很多参数是需要设置的。设置好参数,可以让连接池发挥的更好。事半功倍的效果。--> <!--具体连接池当中的参数如何配置呢?需要反复的根据当前业务情况进行测试。--> <!--poolMaximumActiveConnections:连接池当中最多的正在使用的连接对象的数量上限。最多有多少个连接可以活动。默认值10--> <property name="poolMaximumActiveConnections" value="10"/> <!--每隔2秒打印日志,并且尝试获取连接对象--> <property name="poolTimeToWait" value="2000"/> <!--强行让某个连接空闲,超时时间的设置--> <property name="poolMaximumCheckoutTime" value="10000"/> <!--最多的空闲数量--> <!-- 假设最多的连接数量为10个,最多空闲数量为5个,现在已经空闲5个了,马上第六个也要空闲了 如果第六个空闲下来,连接池为了保证空闲的数量最多5个,会真正关闭多余的空闲连接对象 可以节省系统资源 --> <property name="poolMaximumIdleConnections" value="5"/> </dataSource>
<mappers>
<!-- mapper:配置某个sql映射文件的路径 -->
<!-- resource属性:使用相对于类路径的资源引用方式 -->
<!-- url属性:使用完全限定资源定位符(URL)方式 -->
<mapper resource="CarMapper.xml"/>
</mappers>
<?xml version="1.0" encoding="UTF-8" ?> <!-- 文档类型说明中的 configuration,是根标签的名称,一个文档一个根标签 --> <!-- http://mybatis.org/dtd/mybatis-3-config.dtd xml文档的dtd约束,约束文档中可以出现什么标签、标签能有什么子标签、标签中可以有什么属性以及标签出现的顺序 --> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- configuration:根标签,表示配置信息。 --> <configuration> <!--java.util.Properties类。是一个Map集合。key和value都是String类型--> <!-- property标签中的name属性为key,value属性为value --> <!--在properties标签中可以配置很多属性--> <!-- 在下面的标签中可以使用property配置的属性值,使用 ${property的name属性} 取出相应的值 --> <!--<properties>--> <!--这是其中的一个属性--> <!--<property name="属性名" value="属性值"/>--> <!--<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/> <property name="jdbc.url" value="jdbc:mysql://localhost:3306/powernode"/> <property name="jdbc.username" value="root"/> <property name="jdbc.password" value="root"/>--> <!--</properties>--> <!-- properties中的property可以配置到配置文件中 --> <!--resource,一定是从类路径下开始查找资源--> <properties resource="jdbc.properties" /> <!--从绝对路径当中加载资源。绝对路径怎么写?file:///路径--> <!--<properties url="file:///d:/jdbc.properties" />--> <!-- environments:环境(多个),以“s”结尾表示复数,也就是说mybatis的环境可以配置多个数据源。 --> <!--default表示默认使用的环境。--> <!--默认环境什么意思?当你使用mybatis创建SqlSessionFactory对象的时候,没有指定环境的话,默认使用哪个环境。--> <environments default="powernodeDB"> <!--其中的一个环境。连接的数据库是powernode--> <!--一般一个数据库会对应一个SqlSessionFactory对象。--> <!--一个环境environment会对应一个SqlSessionFactory对象--> <!-- 一个数据库对应一个环境 --> <!-- // 这种方式就是获取的默认环境 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml")); // 这种方式就是通过环境id来使用指定的环境,第二个参数为环境id SqlSessionFactory sqlSessionFactory1 = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "powernodeDB"); --> <environment id="powernodeDB"> <!-- transactionManager标签: 1.作用:配置事务管理器。指定mybatis具体使用什么方式去管理事务。 2.type属性有两个值: 第一个:JDBC: 使用原生的JDBC代码来管理事务。 conn.setAutoCommit(false); .... conn.commit(); 第二个:MANAGED:mybatis不再负责事务的管理,将事务管理交给其它的JEE(JavaEE)容器来管理。例如:spring 3. 大小写无所谓。不缺分大小写。但是不能写其他值。只能是二选一: jdbc、managed 4. 在mybatis中提供了一个事务管理器接口:Transaction 该接口下有两个实现类: JdbcTransaction ManagedTransaction 如果type="JDBC",那么底层会实例化JdbcTransaction对象。 如果type="MANAGED",那么底层会实例化ManagedTransaction --> <transactionManager type="JDBC"/> <!-- dataSource配置: 1.dataSource被称为数据源。 2.dataSource作用是什么?为程序提供Connection对象。(但凡是给程序提供Connection对象的,都叫做数据源。) 3.数据源实际上是一套规范。JDK中有这套规范:javax.sql.DataSource(这个数据源的规范,这套接口实际上是JDK规定的。) 4.我们自己也可以编写数据源组件,只要实现javax.sql.DataSource接口就行了。实现接口当中所有的方法。这样就有了自己的数据源。 比如你可以写一个属于自己的数据库连接池(数据库连接池是提供连接对象的,所以数据库连接池就是一个数据源)。 5.常见的数据源组件有哪些呢【常见的数据库连接池有哪些呢】? 阿里巴巴的德鲁伊连接池:druid c3p0 dbcp .... 6. type属性用来指定数据源的类型,就是指定具体使用什么方式来获取Connection对象: type属性有三个值:必须是三选一。 type="[UNPOOLED|POOLED|JNDI]" UNPOOLED:不使用数据库连接池技术。每一次请求过来之后,都是创建新的Connection对象。 POOLED:使用mybatis自己实现的数据库连接池。 JNDI:集成其它第三方的数据库连接池。 JNDI是一套规范。谁实现了这套规范呢?大部分的web容器都实现了JNDI规范: 例如:Tomcat、Jetty、WebLogic、WebSphere,这些服务器(容器)都实现了JNDI规范。 JNDI是:java命名目录接口。Tomcat服务器实现了这个规范。 --> <!-- 数据源的type属性指定不同的值,需要配置不太的属性 https://mybatis.net.cn/configuration.html#environments --> <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}"/> <!-- 以下配置数据库连接池的参数 --> <!--提醒:正常使用连接池的话,池中有很多参数是需要设置的。设置好参数,可以让连接池发挥的更好。事半功倍的效果。--> <!--具体连接池当中的参数如何配置呢?需要反复的根据当前业务情况进行测试。--> <!--poolMaximumActiveConnections:连接池当中最多的正在使用的连接对象的数量上限。最多有多少个连接可以活动。默认值10--> <property name="poolMaximumActiveConnections" value="10"/> <!--每隔2秒打印日志,并且尝试获取连接对象--> <property name="poolTimeToWait" value="2000"/> <!--强行让某个连接空闲,超时时间的设置--> <property name="poolMaximumCheckoutTime" value="10000"/> <!--最多的空闲数量--> <!-- 假设最多的连接数量为10个,最多空闲数量为5个,现在已经空闲5个了,马上第六个也要空闲了 如果第六个空闲下来,连接池为了保证空闲的数量最多5个,会真正关闭多余的空闲连接对象 可以节省系统资源 --> <property name="poolMaximumIdleConnections" value="5"/> </dataSource> </environment> <!--这是mybatis的另一个环境,也就是连接的数据库是另一个数据库mybatis--> <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:在mappers标签中可以配置多个sql映射文件的路径。 --> <mappers> <!-- mapper:配置某个sql映射文件的路径 --> <!-- resource属性:使用相对于类路径的资源引用方式 --> <!-- url属性:使用完全限定资源定位符(URL)方式 --> <mapper resource="CarMapper.xml"/> </mappers> </configuration>
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);
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
</web-app>
<packaging>war</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> <!-- servlet依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <!-- logback依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.5</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<?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="AccountMapper.xml"/> </mappers> </configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="account">
</mapper>
<?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>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/dbtest
jdbc.username=root
jdbc.password=root
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>银行账户转账</title> </head> <body> <!--/bank是应用的根,部署web应用到tomcat的时候一定要注意这个名字--> <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>
package cw.study.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; /** * ClassName: SqlSessionUtil * Package: cw.study.mybatis.utils * Description: * * @Author tcw * @Create 2023-05-28 14:32 * @Version 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); } } /** * 获取会话对象。 * @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; } }
static {
try {
// 获取默认数据库环境对应的SqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
// 默认数据库环境对应的SqlSessionFactory
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
// 获取默认数据库环境对应的SqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}
<dependencies>
<!-- javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.1-GA</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
java.lang.reflect.InaccessibleObjectException:
Unable to make protected final java.lang.Class
java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
throws java.lang.ClassFormatError accessible:
module java.base does not "opens java.lang" to unnamed module @28c97a5
import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import java.lang.reflect.Method; /** * ClassName: Test * Package: PACKAGE_NAME * Description: * * @Author tcw * @Create 2023-05-31 15:54 * @Version 1.0 */ public class Test { @org.junit.Test public void tset01() throws Exception { // 获取类池,用于生成class ClassPool pool = ClassPool.getDefault(); // 使用类池制造类 // 方法参数为类的全类名 CtClass ctClass = pool.makeClass("cw.mybatis.bank.dao.impl.AccountDaoImpl"); // 类中方法的代码 String methodCode = "public void insert() { System.out.println(\"数据库正在新增信息...\");}"; // 制造类中的方法 // 参数一:类中方法的代码;参数二:为哪个类创建方法 CtMethod ctMethod = CtMethod.make(methodCode, ctClass); // 将创建的方法添加到类中 ctClass.addMethod(ctMethod); // 在内存中动态生成class,将类加载到JVM中 // 得到类对应的Class对象 // Class<?> aClass = ctClass.toClass(); ctClass.toClass(); // 加载类到JVM中 Class<?> aClass = Class.forName("cw.mybatis.bank.dao.impl.AccountDaoImpl"); // 利用动态生成的类进行对象的创建(使用无参构造方法) Object o = aClass.getDeclaredConstructor().newInstance(); // 获取类中的方法 Method insertMethod = aClass.getDeclaredMethod("insert"); // 调用新创建的对象的insert方法 insertMethod.invoke(o); } }
public interface AccountDao {
void delete();
}
@org.junit.Test public void testGenerateImpl() throws Exception { // 获取类型用于类的制造 ClassPool pool = ClassPool.getDefault(); // 制造类 CtClass AccountDaoImpl = pool.makeClass("cw.study.mybatis.dao.impl.AccountDaoImpl"); // 制造接口 CtClass AccountDao = pool.makeInterface("cw.study.mybatis.dao.AccountDao"); // 添加接口到类中 AccountDaoImpl.addInterface(AccountDao); // 实现接口中的方法 // 方法的代码 String methodCode = "public void delete() {System.out.println(\"数据库正在删除信息...\");}"; // 制造AccountDaoImpl类的方法 CtMethod method = CtMethod.make(methodCode, AccountDaoImpl); // 将方法添加到类中 AccountDaoImpl.addMethod(method); // 在内存中生成类,并将类加载到JVM中 Class<?> AccountDaoImplClass = AccountDaoImpl.toClass(); // 创建对象 AccountDao accountDao = (AccountDao) AccountDaoImplClass.getDeclaredConstructor().newInstance(); // 调用方法 accountDao.delete(); }
public interface AccountDao {
void delete();
int insert(String actno);
int update(String actno, Double balance);
String selectByActno(String actno);
}
@org.junit.Test public void testGenerateAccountDaoImpl() throws Exception { // 获取类池 ClassPool pool = ClassPool.getDefault(); // 制造类 CtClass ctClass = pool.makeClass("cw.study.mybatis.dao.impl.AccountDaoImpl"); // 制造接口 CtClass ctInterface = pool.makeInterface("cw.study.mybatis.dao.AccountDao"); // 类实现接口 ctClass.addInterface(ctInterface); // 获取接口中的所有方法 Method[] methods = AccountDao.class.getDeclaredMethods(); // 遍历接口中的每个方法实现接口方法 Arrays.stream(methods).forEach(method -> { // 实现接口方法 // public void update(String actno, Double balance) {} StringBuilder builder = new StringBuilder(); // 添加访问权限修饰符 builder.append("public "); // 添加返回值类型 builder.append(method.getReturnType().getName()); builder.append(" "); // 追加方法名 builder.append(method.getName()); builder.append("("); // 获取方法的形参列表 Parameter[] parameters = method.getParameters(); // 遍历追加形参列表 for (int i = 0; i < parameters.length; i++) { // 追加形参列表中形参的类型 builder.append(parameters[i].getType().getName()); builder.append(" "); // 追加形参名 parameters[i].getName() 获取的形参名为 arg0 arg1 ... builder.append(parameters[i].getName()); if (i != parameters.length - 1) { builder.append(", "); } } builder.append(") {"); // 添加方法体的方法 builder.append("System.out.println(\"" + method.getName() + "\");"); // 动态添加return语句 // 获取返回类型的简类名 String returnTypeSimpleName = method.getReturnType().getSimpleName(); if ("void".equals(returnTypeSimpleName)) { } else if ("int".equals(returnTypeSimpleName)) { builder.append("return 111;"); } else if ("String".equals(returnTypeSimpleName)) { builder.append("return \"method return string\";"); } builder.append("}"); // System.out.println(builder); try { // 制造ctClass的方法 CtMethod method1 = CtMethod.make(builder.toString(), ctClass); // 将方法添加到类中 ctClass.addMethod(method1); } catch (CannotCompileException e) { e.printStackTrace(); } }); // 在内存中生成类并将类加载到JVM Class<?> aClass = ctClass.toClass(); // 创建对象(接口的实现类可以进行强转) AccountDao accountDao = (AccountDao) aClass.getDeclaredConstructor().newInstance(); // 调用方法 accountDao.delete(); accountDao.insert(""); accountDao.selectByActno(""); accountDao.update("", 12.0); }
package cw.study.mybatis.utils; import org.apache.ibatis.javassist.CannotCompileException; import org.apache.ibatis.javassist.ClassPool; import org.apache.ibatis.javassist.CtClass; import org.apache.ibatis.javassist.CtMethod; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.session.SqlSession; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Arrays; /** * ClassName: GenerateDaoProxy * Package: cw.study.mybatis.utils * Description: * 该工具类用于动态生成实现Dao接口的代理类 * GenerateDaoProxy 这个工具类是MyBatis框架提供的 * * @Author tcw * @Create 2023-05-31 17:42 * @Version 1.0 */ public class GenerateDaoProxy { /** * 工具类的构造方法私有化,防止创建对象 */ private GenerateDaoProxy() {} /** * 动态生成Dao接口的代理类对象 * * @param sqlSession 与数据库的会话对象,由工具类的使用这传递 * @param daoInterfaceClass Dao接口 * @return Dao接口的代理类对象 */ public static Object generate(SqlSession sqlSession, Class daoInterfaceClass) { // 获取类池 ClassPool pool = ClassPool.getDefault(); // 制造接口的代理类 CtClass daoInterfaceImpl = pool.makeClass(daoInterfaceClass.getName() + "Impl"); // 制造接口 CtClass daoInterface = pool.makeInterface(daoInterfaceClass.getName()); // 接口的代理类实现接口 daoInterfaceImpl.addInterface(daoInterface); // 接口的代理类实现接口中的方法 // 获取接口中所有的方法 Method[] daoInterfaceClassDeclaredMethods = daoInterfaceClass.getDeclaredMethods(); // 遍历接口中的所有的方法,逐个进行实现,并添加到接口的代理类中 Arrays.stream(daoInterfaceClassDeclaredMethods).forEach(method -> { // 实现接口中的方法 try { // 接口方法的实现方法的代码 StringBuilder methodCode = new StringBuilder(); // public int update(String actno, Double balance) {代码;} methodCode.append("public "); // 获取接口方法的返回值类型,添加到实现方法中 methodCode.append(method.getReturnType().getName()); methodCode.append(" "); // 获取接口方法的方法名,添加到实现方法中 methodCode.append(method.getName()); methodCode.append("("); // 获取接口方法的形参列表,添加到实现方法中 Parameter[] methodParameters = method.getParameters(); for (int i = 0; i < methodParameters.length; i++) { // 获取形参的类型 methodCode.append(methodParameters[i].getType().getName()); methodCode.append(" "); // 获取形参的名 arg0 arg1 arg2 ... methodCode.append(methodParameters[i].getName()); // 如果不是最后一个形参需要在后面添加逗号 if (i != methodParameters.length - 1) { methodCode.append(", "); } } methodCode.append(")"); methodCode.append("{\n"); // 接口方法的具体实现 // 获取与数据库的会话对象 // javassist 需要知道类是哪个包下的所以要使用全限定包名 methodCode.append("\torg.apache.ibatis.session.SqlSession sqlSession = cw.study.mybatis.utils" + ".SqlSessionUtil.openSession();\n"); // 执行SQL语句,我们需要知道SQL语句的类型才可以调用相应的方法执行SQL // getConfiguration() 获取MyBatis核心配置文件中根标签configuration中的内容 // getMappedStatement(SQL语句的Id) 根据SQL语句的id获取相应的SQL语句 // getSqlCommandType() 获取SQL语句的类型,获取到SQL语句的类型是一个枚举值 // 由于SQL语句的id是框架的使用者提供的,所以框架的开发者不可能知道SQL语句的id // 因此框架的开发者就规定SQL语句的id必须时dao接口中的方法名,namespace必须是dao接口的全限定类名 // 否则不能使用框架提供的动态代理生成接口的实现类的功能 // SQL id = namespace + . +SQL id String SqlId = daoInterfaceClass.getName() + "." + method.getName(); SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(SqlId).getSqlCommandType(); // 根据SQL语句的类型的不同调用相应的方法 if (sqlCommandType == SqlCommandType.INSERT) { } else if (sqlCommandType == SqlCommandType.DELETE) { } else if (sqlCommandType == SqlCommandType.UPDATE) { methodCode.append("\treturn sqlSession.update(\"" + SqlId + "\", arg0);\n"); } else if (sqlCommandType == SqlCommandType.SELECT) { // by java.lang.reflect.InvocationTargetException methodCode.append("\treturn (" + method.getReturnType().getName() + ") sqlSession.selectOne(\"" + SqlId + "\", arg0);\n"); } methodCode.append("}"); // System.out.println(methodCode); // 制造接口方法对应的实现方法 CtMethod ctMethod = CtMethod.make(methodCode.toString(), daoInterfaceImpl); // 将制造的实现方法添加到动态生成的代理类中 daoInterfaceImpl.addMethod(ctMethod); } catch (Exception e) { e.printStackTrace(); } }); // 接口动态代理类的对象 Object daoInterfaceProxyObj = null; try { // 在内存中动态生成类,并且将动态生成的类添加到JVM中 Class<?> daoInterfaceImplClass = daoInterfaceImpl.toClass(); // 创建接口代理类的对象 daoInterfaceProxyObj = daoInterfaceImplClass.getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); } // 返回接口动态代理类的对象 return daoInterfaceProxyObj; } }
<!-- 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>
package cw.study.mybatis.pojo; /** * ClassName: Account * Package: cw.study.mybatis.pojo * Description: * * @Author tcw * @Create 2023-05-28 14:33 * @Version 1.0 */ 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; } }
package cw.study.mybatis.dao; import cw.study.mybatis.pojo.Account; /** * ClassName: AccountDao * Package: cw.study.mybatis.dao * Description: * * @Author tcw * @Create 2023-05-31 16:44 * @Version 1.0 */ public interface AccountDao { Account selectByActno(String actno); }
<?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="cw.study.mybatis.dao.AccountDao">
<select id="selectByActno" resultType="cw.study.mybatis.pojo.Account">
select * from t_act where actno = #{actno}
</select>
</mapper>
@org.junit.Test
public void testGenerateDaoProxy() {
SqlSession sqlSession = SqlSessionUtil.openSession();
AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(sqlSession, AccountDao.class);
Account act001 = accountDao.selectByActno("act001");
System.out.println(act001);
}
@org.junit.Test
public void testGetMapper() {
SqlSession sqlSession = SqlSessionUtil.openSession();
// GenerateDaoProxy.generate(sqlSession, AccountDao.class);
// 需要一个sqlSession用于获取SQL id,AccountDao.class 需要代理实现的接口
// sqlSession.getMapper(AccountDao.class)
// 调用该方法的就是用于获取SQL id的sqlSession,AccountDao.class 需要代理实现的接口
// 该方法返回dao接口的代理实现类对象实例
// 要使用该方法,SQL语句的id必须时dao接口中的方法名,namespace必须是dao接口的全限定类名
AccountDao accountDaoProxy = sqlSession.getMapper(AccountDao.class);
Account act001 = accountDaoProxy.selectByActno("act001");
System.out.println(act001);
}
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; /** * ClassName: SqlSessionUtil * Package: cw.mybatis.utils * Description: * * @Author tcw * @Create 2023-05-23 21:40 * @Version 1.0 */ public class SqlSessionUtil { private static SqlSessionFactory sqlSessionFactory = null; // 默认使用数据库环境对应的SqlSessionFactory private static ThreadLocal<SqlSession> local = new ThreadLocal<>(); // 保证一个线程对应一个SqlSession static { try { // 获取默认使用数据库环境对应的SqlSessionFactory对象 sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config" + ".xml")); } catch (IOException e) { e.printStackTrace(); } } /** * 私有化构造器,防止创建工具类实例对象 */ private SqlSessionUtil() {} /** * 使用工厂对象开启本线程和数据库的会话 * * @return 与数据库的会话对象 */ public static SqlSession openSession() { SqlSession sqlSession = local.get(); // 获取当前线程对应的数据库会话对象 // 如果当前线程没有对应的数据库会话对象,则创建一个会话对象 if (sqlSession == null) { sqlSession = sqlSessionFactory.openSession(); // 保存本线程对应的数据库会话对象 // 将sqlSession对象绑定到当前线程上。 local.set(sqlSession); } return sqlSession; } /** * 关闭会话对象 * 关闭SqlSession对象并从当前线程中移除SqlSession对象 */ public static void close() { SqlSession sqlSession = local.get(); // 获取当前线程对应的数据库会话对象 // 如果当前线程有相应的数据库会话对象就进行关闭 if (sqlSession != null) { sqlSession.close(); // 注意移除SqlSession对象和当前线程的绑定关系。 // 因为Tomcat服务器支持线程池。也就是说:用过的线程对象t1,可能下一次还会使用这个t1线程。 local.remove(); } } }
package cw.mybatis.pojo; /** * ClassName: Car * Package: cw.mybatis.pojo * Description: * 封装汽车相关信息的pojo类,普通的java类。 * * @Author tcw * @Create 2023-05-23 21:59 * @Version 1.0 */ 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; } @Override public String toString() { return "Car{" + "id=" + id + ", carNum='" + carNum + '\'' + ", brand='" + brand + '\'' + ", guidePrice=" + guidePrice + ", produceTime='" + produceTime + '\'' + ", carType='" + carType + '\'' + '}'; } }
package cw.mybatis.mapper; import cw.mybatis.pojo.Car; import java.util.List; /** * ClassName: CarMapper * Package: cw.mybatis.mapper * Description: * * @Author tcw * @Create 2023-05-23 21:57 * @Version 1.0 */ public interface CarMapper { /** * 新增 car * * @param car 需要新增的 car 信息 * @return 影响数据库中的条数 */ int insert(Car car); /** * 根据 id 删除汽车信息 * * @param id 汽车信息对应的 id * @return 影响数据库中的条数 */ int deleteById(Long id); /** * 修改汽车信息 * * @param car 修改之后的汽车信息 * @return 影响数据库中的条数 */ int update(Car car); /** * 根据汽车 id 查询汽车信息 * * @param id 汽车信息的 id * @return 汽车信息 */ Car selectById(Long id); /** * 查询所有的汽车信息 * * @return 所有的汽车信息 */ List<Car> selectAll(); }
<?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"> <!-- 如果要使用面向接口的方式进行CRUD,则namespace的属性值必须为相应接口的全类名 --> <!-- SQL语句的id必须为方法名 --> <mapper namespace="cw.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="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> <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> </mapper>
package cw.mybatis.mapper; import cw.mybatis.pojo.Car; import cw.mybatis.utils.SqlSessionUtil; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; import static org.junit.Assert.*; /** * ClassName: CarMapperTest * Package: cw.mybatis.mapper * Description: * * @Author tcw * @Create 2023-05-23 22:15 * @Version 1.0 */ public class CarMapperTest { @Test public void insert() { SqlSession sqlSession = SqlSessionUtil.openSession(); // 面向接口,获取接口的代理对象 CarMapper mapper = sqlSession.getMapper(CarMapper.class); // 数据对应的对象 Car car = new Car(null, "4444", "奔驰C200", 32.0, "2000-10-10", "新能源"); // 调用定义的新增car的方法 int insert = mapper.insert(car); System.out.println(insert); sqlSession.commit(); SqlSessionUtil.close(); } @Test public void deleteById() { SqlSession sqlSession = SqlSessionUtil.openSession(); CarMapper mapper = sqlSession.getMapper(CarMapper.class); int i = mapper.deleteById(1L); System.out.println(i); sqlSession.commit(); SqlSessionUtil.close(); } @Test public void update() { SqlSession sqlSession = SqlSessionUtil.openSession(); CarMapper mapper = sqlSession.getMapper(CarMapper.class); Car car = new Car(20L, "4444", "奔驰C200&&&", 32.0, "2000-10-10", "新能源"); int update = mapper.update(car); System.out.println(update); sqlSession.commit(); SqlSessionUtil.close(); } @Test public void selectById() { SqlSession sqlSession = SqlSessionUtil.openSession(); CarMapper mapper = sqlSession.getMapper(CarMapper.class); Car car = mapper.selectById(20L); System.out.println(car); SqlSessionUtil.close(); } @Test public void selectAll() { SqlSession sqlSession = SqlSessionUtil.openSession(); List<Car> cars = sqlSession.getMapper(CarMapper.class).selectAll(); cars.forEach(System.out::println); SqlSessionUtil.close(); } }
/**
* 根据汽车类型获取汽车信息
*
* @param carType 汽车类型
* @return 汽车信息组成的列表
*/
List<Car> selectByCarType(String carType);
<select id="selectByCarType" 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 car_type = #{carType} </select> <select id="selectByCarType" 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 car_type = ${carType} </select>
@Test
public void test01() {
SqlSession sqlSession = SqlSessionUtil.openSession();
// 获取CarMapper接口的代理类对象
// 底层为CarMapper接口生成了字节码,同时创建了实现类的对象
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectByCarType("新能源");
cars.forEach(System.out::println);
SqlSessionUtil.close();
}
[main] DEBUG cw.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 cw.mybatis.mapper.CarMapper.selectByCarType - ==> Parameters: 新能源(String) [main] DEBUG cw.mybatis.mapper.CarMapper.selectByCarType - <== Total: 2 查询出来的数据: Car{id=2, carNum='1002', brand='奔驰E300L', guidePrice=55.0, produceTime='2022-11-11', carType='新能源'} Car{id=20, carNum='4444', brand='奔驰C200&&&', guidePrice=32.0, produceTime='2000-10-10', carType='新能源'}
[main] DEBUG cw.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 cw.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'
/**
* 查询所有的汽车信息,并进行升序或降序排序
*
* @param ascOrDesc 升序或降序排序
* @return 排序后的汽车信息组成的集合
*/
List<Car> selectAllAscOrDesc(String ascOrDesc);
<select id="selectAllAscOrDesc" 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 order by produce_time #{ascOrDesc}; </select> <select id="selectAllAscOrDesc" 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 order by produce_time ${ascOrDesc}; </select>
@Test
public void test02() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 查询所有的汽车信息,并降序排序
List<Car> cars = mapper.selectAllAscOrDesc("desc");
cars.forEach(System.out::println);
SqlSessionUtil.close();
}
[main] DEBUG cw.mybatis.mapper.CarMapper.selectAllAscOrDesc - ==> 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 ?; [main] DEBUG cw.mybatis.mapper.CarMapper.selectAllAscOrDesc - ==> Parameters: desc(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 'desc'; 存在语法错误 org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''desc'' at line 8 ### 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 order by produce_time ?; ### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''desc'' at line 8
[main] DEBUG cw.mybatis.mapper.CarMapper.selectAllAscOrDesc - ==>
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 desc;
符合预期,可以进行数据的查询并进行排序
[main] DEBUG cw.mybatis.mapper.CarMapper.selectAllAscOrDesc - ==> Parameters:
[main] DEBUG cw.mybatis.mapper.CarMapper.selectAllAscOrDesc - <== Total: 17
use dbtest; create table t_log_20220901 ( id int primary key auto_increment, log varchar(255), time datetime ); INSERT INTO `t_log_20220901` (`id`, `log`, `time`) VALUES (1, '日志信息日志信息日志信息', '2022-09-01 11:06:31'); INSERT INTO `t_log_20220901` (`id`, `log`, `time`) VALUES (2, '安全警告安全警告安全警告', '2022-09-01 11:07:20'); create table `t_log_20220902` ( id int primary key auto_increment, log varchar(255), time datetime ); INSERT INTO `t_log_20220902` (`id`, `log`, `time`) VALUES (1, '插入数据', '2022-09-02 11:08:31'); INSERT INTO `t_log_20220902` (`id`, `log`, `time`) VALUES (2, '删除数据', '2022-09-02 11:08:45');
package cw.mybatis.pojo; /** * ClassName: Log * Package: cw.mybatis.pojo * Description: * * @Author tcw * @Create 2023-05-25 11:24 * @Version 1.0 */ public class Log { private Integer id; private String log; private String time; public Log() { } public Log(Integer id, String log, String time) { this.id = id; this.log = log; this.time = time; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLog() { return log; } public void setLog(String log) { this.log = log; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } @Override public String toString() { return "Log{" + "id=" + id + ", log='" + log + '\'' + ", time='" + time + '\'' + '}'; } }
public interface LogMapper {
/**
* 根据日期查询不同的数据库表中的全部信息
*
* @param date 日期字符串
* @return 与日期对应的数据表中全部信息组成的集合
*/
List<Log> selectAllByTable(String date);
}
<mapper namespace="cw.mybatis.mapper.LogMapper">
<select id="selectAllByTable" resultType="cw.mybatis.pojo.Log">
select id, log, `time`
from t_log_${date}
</select>
</mapper>
<mappers>
<mapper resource="CarMapper.xml"/>
<mapper resource="LogMapper.xml"/>
</mappers>
@Test
public void selectAllByTable() {
SqlSession sqlSession = SqlSessionUtil.openSession();
// 获取代理对象
LogMapper mapper = sqlSession.getMapper(LogMapper.class);
// 查询数据
List<Log> logs20220901 = mapper.selectAllByTable("20220901");
logs20220901.forEach(System.out::println); // 打印信息
List<Log> logs20220902 = mapper.selectAllByTable("20220902");
logs20220902.forEach(System.out::println); // 打印信息
// 关闭资源
SqlSessionUtil.close();
}
直接将日期字符串以字符串拼接的方式与SQL语句进行拼接,然后再进行SQL语句的编译 [main] DEBUG cw.mybatis.mapper.LogMapper.selectAllByTable - ==> Preparing: select id, log, `time` from t_log_20220901 [main] DEBUG cw.mybatis.mapper.LogMapper.selectAllByTable - ==> Parameters: [main] DEBUG cw.mybatis.mapper.LogMapper.selectAllByTable - <== Total: 2 Log{id=1, log='日志信息日志信息日志信息', time='2022-09-01 11:06:31'} Log{id=2, log='安全警告安全警告安全警告', time='2022-09-01 11:07:20'} [main] DEBUG cw.mybatis.mapper.LogMapper.selectAllByTable - ==> Preparing: select id, log, `time` from t_log_20220902 [main] DEBUG cw.mybatis.mapper.LogMapper.selectAllByTable - ==> Parameters: [main] DEBUG cw.mybatis.mapper.LogMapper.selectAllByTable - <== Total: 2 Log{id=1, log='插入数据', time='2022-09-02 11:08:31'} Log{id=2, log='删除数据', time='2022-09-02 11:08:45'} 如果是 #{} 会先进行SQL语句的编译, 编译后的SQL语句为:Preparing: select id, log, `time` from t_log_? 使用日期字符串填充后,SQL语句变为:Preparing: select id, log, `time` from t_log_'20220902'
-- 第一种 or :
delete from t_car where id=1 or id=2 or id=3;
-- 第二种 in :
delete from t_car where id in(1,2,3);
/**
* 根据id批量删除数据库表中的信息
*
* @param ids 需要进行删除操作的信息的id组成的字符串
* @return 删除记录的条数
*/
int deleteBatch(String ids);
<delete id="deleteBatch">
delete from t_car
where id in (${ids});
</delete>
<!--
如果要采用 delete from t_car where id=1 or id=2 or id=3;
这种形式则需要使用动态SQL
-->
@Test
public void test03() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
int deleteCount = mapper.deleteBatch("2, 3, 4");
System.out.println(deleteCount);
sqlSession.commit()
SqlSessionUtil.close();
}
先进行字符串拼接,然后再进行SQL语句的编译
[main] DEBUG cw.mybatis.mapper.CarMapper.deleteBatch - ==>
Preparing: delete from t_car where id in (2, 3, 4);
[main] DEBUG cw.mybatis.mapper.CarMapper.deleteBatch - ==> Parameters:
[main] DEBUG cw.mybatis.mapper.CarMapper.deleteBatch - <== Updates: 3
如果使用 #{} 先进行SQL语句的编译
编译后的SQL为 delete from t_car where id in (?);
使用查询参数填充占位符后SQL为 delete from t_car where id in ('2, 3, 4');
会报语法错误
select * from t_car where brand like '%奔驰%';
select * from t_car where brand like '%比亚迪%';
'%?%'
不会被认为是JDBC的占位符,而会被认为是字符串的一部分'%${brand}%'
,字符串拼接后为 '%brand%'
concat('%',#{brand},'%')
,SQL编译后为 concat('%', ?,'%')
concat('%','${brand}','%')
,字符串拼接后为 concat('%','brand','%')
"%"#{brand}"%"
,SQL编译后为 "%"?"%"
'%宝马%'
,将这一整个字符串传递/**
* 根据汽车的品牌进行模糊查询
*
* @param brand 汽车品牌字符串
* @return 查询出来的汽车信息
*/
List<Car> selectByBrandLike(String brand);
<select id="selectByBrandLike" 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 brand like concat('%', #{brand}, '%');
</select>
@Test
public void test04() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectByBrandLike("比亚迪");
cars.forEach(System.out::println);
SqlSessionUtil.close();
}
先进行SQL语句的编译 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 concat('%', ?, '%'); 使用数据填充占位符(采用 #{} ) 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 concat('%', '比亚迪', '%'); [main] DEBUG cw.mybatis.mapper.CarMapper.selectByBrandLike - ==> Preparing: 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 concat('%', ?, '%'); [main] DEBUG cw.mybatis.mapper.CarMapper.selectByBrandLike - ==> Parameters: 比亚迪(String) [main] DEBUG cw.mybatis.mapper.CarMapper.selectByBrandLike - <== Total: 5 Car{id=9, carNum='1111', brand='比亚迪汉', guidePrice=10.0, produceTime='2020-11-11', carType='电车'} Car{id=15, carNum='1333', brand='比亚迪汉', guidePrice=10.0, produceTime='2020-11-11', carType='电车'} Car{id=16, carNum='1333', brand='比亚迪汉', guidePrice=10.0, produceTime='2020-11-11', carType='电车'} Car{id=17, carNum='1333', brand='比亚迪汉', guidePrice=10.0, produceTime='2020-11-11', carType='电车'} Car{id=18, carNum='1333', brand='比亚迪汉', guidePrice=10.0, produceTime='2020-11-11', carType='电车'}
<select id="selectByCarType" resultType="cw.mybatis.pojo.Car"><select/>
<select id="selectAllAscOrDesc" resultType="cw.mybatis.pojo.Car"><select/>
<select id="selectByBrandLike" resultType="cw.mybatis.pojo.Car"><select/>
typeAliases
标签中使用 typeAliase
标签起别名
<!-- 起别名 -->
<typeAliases>
<!-- 为 cw.mybatis.pojo.Car 起别名,别名为 Car -->
<typeAlias type="cw.mybatis.pojo.Car" alias="Car"/>
</typeAliases>
<select id="selectByCarType" 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 car_type = #{carType}
</select>
@Test
public void test01() {
SqlSession sqlSession = SqlSessionUtil.openSession();
// 获取CarMapper接口的代理类对象
// 底层为CarMapper接口生成了字节码,同时创建了实现类的对象
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectByCarType("新能源");
cars.forEach(System.out::println);
SqlSessionUtil.close();
}
<select id="selectByCarType" resultType="caR">
<!-- 起别名 -->
<typeAliases>
<!-- 为 cw.mybatis.pojo.Car 起别名,别名为 Car -->
<typeAlias type="cw.mybatis.pojo.Car" alias="Car"/>
<typeAlias type="cw.mybatis.mapper.CarMapper" alias="CarMapper"/>
</typeAliases>
<mapper namespace="CarMapper">
<!-- 起别名 -->
<typeAliases>
<!-- 为 cw.mybatis.pojo.Car 起别名,别名为 Car -->
<typeAlias type="cw.mybatis.pojo.Car"/>
</typeAliases>
<select id="selectByCarType" resultType="caR">
<!-- 起别名 -->
<typeAliases>
<!-- 为 cw.mybatis.pojo 包下面的所有类起别名,别名默认为类的简名 -->
<package name="cw.mybatis.pojo"/>
</typeAliases>
<select id="selectByCarType" resultType="cAR">
<mapper resource="CarMapper.xml"/>
<mapper url="file:///d:/CarMapper.xml"/>
<mapper class="全限定接口名,带有包名"/>
<mapper class="com.powernode.mybatis.mapper.CarMapper"/>
,mybatis框架会自动去com/powernode/mybatis/mapper目录下查找CarMapper.xml文件com.powernode.mybatis.mapper
这个会被整个当成一个目录名,resources目录下没有建包的概念,只有创建目录的概念 <mappers>
<!-- 指定 xml 文件放在 com.powernode.mybatis.mapper 包下 -->
<package name="com.powernode.mybatis.mapper"/>
</mappers>
<?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=""/> <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>
<?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="">
</mapper>
/**
* 插入汽车信息,同时使用生成的主键值
*
* @param car 汽车信息
* @return 影响数据库的条数
*/
int insertCarUseGeneratedKey(Car car);
<!--
useGeneratedKeys="true" 开启使用自动生成的主键
keyProperty="id" 指定要将自动生成的主键值保存到用于
传递数据给SQL语句的对象的哪个属性上
-->
<insert id="insertCarUseGeneratedKey" useGeneratedKeys="true" keyProperty="id">
insert into t_car
values (null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType});
</insert>
@Test
public void testInsertCarUseGeneratedKey() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = new Car(null, "9199", "凯美瑞", 32.0, "2020-11-12", "电车");
int count = mapper.insertCarUseGeneratedKey(car);
System.out.println("影响数据库的条数:" + count);
System.out.println(car);
SqlSessionUtil.close();
}
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', '女');
package cw.study.mybatis.pojo; import java.util.Date; /** * ClassName: Student * Package: cw.study.mybatis.pojo * Description: * * @Author tcw * @Create 2023-05-28 11:49 * @Version 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 标签中有个 parameterType属性
告诉mybatis框架,我这个方法的参数类型是什么类型。
mybatis框架自身带有类型自动推断机制,所以大部分情况下parameterType属性都是可以省略不写的。
-->
<select id="selectById" resultType="Student" parameterType="java.lang.Long">
select * from t_student where id = #{id}
</select>
SQL语句编译后:select * from t_student where id = ?
JDBC代码是一定要给 ? 传值的,会调用 ps.setXxx(第几个问号, 传什么值);
ps.setLong(1, 1L);
ps.setString(1, "zhangsan");
ps.setDate(1, new Date());
ps.setInt(1, 100);
...
<!-- mybatis框架实际上内置了很多别名,可以参考开发手册 -->
<select id="selectByName" resultType="Student" parameterType="string">
select * from t_student where name = #{name}
</select>
<select id="selectByName" resultType="Student" parameterType="string">
select * from t_student where name = #{name, javaType=String, jdbcType=VARCHAR}
</select>
package cw.study.mybatis.mapper; import cw.study.mybatis.pojo.Student; import java.util.Date; import java.util.List; /** * ClassName: StudentMapper * Package: cw.study.mybatis.mapper * Description: * * @Author tcw * @Create 2023-05-28 11:55 * @Version 1.0 */ public interface StudentMapper { // 根据id查询 List<Student> selectById(Long id); // name查询 List<Student> selectByName(String name); // birth查询 List<Student> selectByBirth(Date birth); // sex查询 List<Student> selectBySex(Character sex); }
<?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="cw.study.mybatis.mapper.StudentMapper"> <!-- select 标签中有个 parameterType属性 告诉mybatis框架,我这个方法的参数类型是什么类型。 mybatis框架自身带有类型自动推断机制,所以大部分情况下parameterType属性都是可以省略不写的。 --> <select id="selectById" resultType="Student" parameterType="java.lang.Long"> select * from t_student where id = #{id} </select> <!-- mybatis框架实际上内置了很多别名,可以参考开发手册 --> <select id="selectByName" resultType="Student" parameterType="string"> select * from t_student where name = #{name, javaType=String, jdbcType=VARCHAR} </select> <select id="selectByBirth" resultType="Student" parameterType="date"> select * from t_student where birth = #{birth} </select> <select id="selectBySex" resultType="Student"> select * from t_student where sex = #{sex} </select> </mapper>
/** * ClassName: StudentMapperTest * Package: cw.study.mybatis.test * Description: * * @Author tcw * @Create 2023-05-28 11:59 * @Version 1.0 */ public class StudentMapperTest { @Test public void testSelectById() { SqlSession sqlSession = SqlSessionUtil.openSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> students = mapper.selectById(1L); students.forEach(System.out::println); SqlSessionUtil.close(); } @Test public void testSelectByName() { SqlSession sqlSession = SqlSessionUtil.openSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> students = mapper.selectByName("李四"); students.forEach(System.out::println); SqlSessionUtil.close(); } @Test public void testSelectByBirth() throws ParseException { SqlSession sqlSession = SqlSessionUtil.openSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); List<Student> students = mapper.selectByBirth(simpleDateFormat.parse("2001-11-22")); students.forEach(System.out::println); SqlSessionUtil.close(); } @Test public void testSelectBySex() { SqlSession sqlSession = SqlSessionUtil.openSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> students = mapper.selectBySex('男'); students.forEach(System.out::println); SqlSessionUtil.close(); } }
#{map集合的key}
来取值。/**
* 通过集合传递要插入数据库中的学生信息
*
* @param map 学生信息
* @return 影响数据库的条数
*/
int insertStudentByMap(Map<String, Object> map);
<!--
parameterType="map" 可以省略, 可以自动推出传入的参数为map集合
在使用集合中的数据的时候需要通过#{map集合的key}来取值。
-->
<insert id="insertStudentByMap" parameterType="map">
insert into t_student(id, name, age, sex, height, birth)
values (null, #{name}, #{age}, #{sex}, #{height}, #{birth});
</insert>
@Test public void testInsertStudentByMap() { // 封装数据 HashMap<String, Object> map = new HashMap<>(); // name, age, sex, height, birth map.put("name", "张三"); map.put("age", 20); map.put("sex", '男'); map.put("height", 1.80); map.put("birth", new Date()); SqlSession sqlSession = SqlSessionUtil.openSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); int count = mapper.insertStudentByMap(map); System.out.println(count); sqlSession.commit(); SqlSessionUtil.close(); }
#{get方法去掉get首字母小写}
来取值。/**
* 通过实体类传递要插入数据库的学生信息
*
* @param student 学生信息
* @return 影响数据库记录的条数
*/
int insertStudentByPojo(Student student);
<!--
parameterType="student" 可以省略,可以自动推出类型为Student类
可以使用 student 是因为在MyBatis核心配置文件中配置了别名
在使用集合中的数据的时候需要通过#{get方法去掉get首字母小写}来取值。
-->
<insert id="insertStudentByPojo" parameterType="student">
insert into t_student(id, `name`, age, sex, height, birth)
values (null, #{name}, #{age}, #{sex}, #{height}, #{birth});
</insert>
@Test
public void testInsertStudentByPojo() {
// 封装数据
Student student = new Student(null, "李四", 23, 1.78, '男', new Date());
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int count = mapper.insertStudentByPojo(student);
System.out.println(count);
sqlSession.commit();
SqlSessionUtil.close();
}
map.put("arg0", name);
map.put("arg1", sex);
map.put("param1", name);
map.put("param2", sex);
// arg 从 0 开始
#{arg0}
#{arg1}
#{arg2}
// param 从 1 开始
#{param1}
#{param2}
#{param3}
/**
* 根据学生的姓名和性别进行学生信息的查询
*
* @param name 学生的姓名
* @param sex 学生的性别
* @return 学生信息组成的集合
*/
List<Student> selectByNameAndSex(String name, Character sex);
<!-- parameterType="" 不用写了 -->
<select id="selectByNameAndSex" resultType="student">
select * from t_student
where `name` = #{arg0} and sex = #{param2};
</select>
@Test
public void testSelectByNameAndSex() {
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.selectByNameAndSex("张三", '男');
students.forEach(System.out::println);
SqlSessionUtil.close();
}
/**
* 根据学生的姓名和性别进行学生信息的查询
*
* @param name 学生的姓名
* @param sex 学生的性别
* @return 学生信息组成的集合
*/
List<Student> selectByNameAndSexAnnotation(
// 使用@Param注解指定参数在map集合中的key
// @Param注解中的value属性用于指定参数在map集合中的key
@Param("name") String name,
@Param("sex") Character sex
);
map.put("name", name);
map.put("sex", sex);
<select id="selectByNameAndSexAnnotation" resultType="student">
select * from t_student
where `name` = #{name} and sex = #{sex};
</select>
@Test
public void testSelectByNameAndSexAnnotation() {
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.selectByNameAndSexAnnotation("张三", '男');
students.forEach(System.out::println);
SqlSessionUtil.close();
}
<select id="selectByNameAndSexAnnotation" resultType="student">
select * from t_student
where `name` = #{arg0} and sex = #{arg1};
</select>
<select id="selectByNameAndSexAnnotation" resultType="student">
select * from t_student
where `name` = #{param1} and sex = #{param2};
</select>
// mapper 实际上指向了代理对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
// selectByNameAndSexAnnotation 是代理对象 mapper 的代理方法
// 执行到这时,会调用代理对象mapper的代理方法
List<Student> students = mapper.selectByNameAndSexAnnotation("张三", '男');
// Object proxy:代理对象
// Method method:目标方法
// Object[] args:目标方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
// MapperProxy 内部类 PlainMethodInvoker 的方法
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return this.mapperMethod.execute(sqlSession, args);
}
// MapperMethod 的方法 public Object execute(SqlSession sqlSession, Object[] args) { Object result; Object param; // this.command.getType() 获取 SQL 语句的类型(insert|update|select|delete) switch (this.command.getType()) { case INSERT: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); break; case UPDATE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.update(this.command.getName(), param)); break; case DELETE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.delete(this.command.getName(), param)); break; case SELECT: // this.method.returnsVoid() 我们定义的Mapper接口中的方法无返回值 if (this.method.returnsVoid() && this.method.hasResultHandler()) { this.executeWithResultHandler(sqlSession, args); result = null; // 我们定义的Mapper接口中的方法返回多条记录 } else if (this.method.returnsMany()) { result = this.executeForMany(sqlSession, args); } else if (this.method.returnsMap()) { result = this.executeForMap(sqlSession, args); } else if (this.method.returnsCursor()) { result = this.executeForCursor(sqlSession, args); } else { param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + this.command.getName()); } if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) { throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ")."); } else { return result; } }
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { // 将Mapper接口参数组成的数组转换为SQL语句中占位符填充的参数 // 转换成 param1 = value1 param2 = value2 ... 的形式 // 或者 arg1 = value1 arg2 = value2 ... 的形式 // 或者 'name' = value1 'sex' = value2 ... 的形式 Object param = this.method.convertArgsToSqlCommandParam(args); List result; if (this.method.hasRowBounds()) { RowBounds rowBounds = this.method.extractRowBounds(args); result = sqlSession.selectList(this.command.getName(), param, rowBounds); } else { result = sqlSession.selectList(this.command.getName(), param); } if (!this.method.getReturnType().isAssignableFrom(result.getClass())) { return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result); } else { return result; } }
public Object convertArgsToSqlCommandParam(Object[] args) {
return this.paramNameResolver.getNamedParams(args);
}
public Object getNamedParams(Object[] args) { // private final SortedMap<Integer, String> names; // 是一个Map集合,key为第几个参数,value为参数对应的名字 int paramCount = this.names.size(); if (args != null && paramCount != 0) { // 判断方法参数上是否有Param注解 if (!this.hasParamAnnotation && paramCount == 1) { Object value = args[(Integer)this.names.firstKey()]; return wrapToMapIfCollection(value, this.useActualParamName ? (String)this.names.get(0) : null); } else { // 有Param注解走该分支 // 有Param注解会新建一个Map集合 param集合 Map<String, Object> param = new MapperMethod.ParamMap(); int i = 0; // 遍历存储参数名字的Map集合 for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) { Map.Entry<Integer, String> entry = (Map.Entry)var5.next(); // 向param集合中添加数据,key为接口方法参数的名字 entry.getValue() // value为接口方法参数的参数值 args[(Integer)entry.getKey()] param.put(entry.getValue(), args[(Integer)entry.getKey()]); // genericParamName 通用参数名 String genericParamName = "param" + (i + 1); // param1 param2 ... // 集合是否包含通用参数名 param1 param2 ... if (!this.names.containsValue(genericParamName)) { // 这个就是可以使用 param1 param2 获取参数值 // 向集合中添加 param1=value1 param2=value2 param.put(genericParamName, args[(Integer)entry.getKey()]); } } return param; } } else { return null; } }
public interface CarMapper {
/**
* 根据id查询汽车信息
*
* @param id 汽车信息的id
* @return 汽车信息
*/
Car selectById(Long id);
}
<!--
如果SQL语句中不使用别名,
则读取数据后字段名与对象的属性名不一致的话
不一致的属性的值会为null
-->
<select id="selectById" 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 id = #{id};
</select>
@org.junit.Test
public void testSelectById() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = mapper.selectById(5L);
System.out.println(car);
SqlSessionUtil.close();
}
/**
* 查询所有的汽车信息
*
* @return 所有的汽车信息
*/
List<Car> selectAll();
<select id="selectAll" resultType="car">
select id,
car_num as carNum,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from t_car;
</select>
@org.junit.Test
public void testSelectAll() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAll();
cars.forEach(System.out::println);
SqlSessionUtil.close();
}
/**
* 根据id查询汽车信息
*
* @param id 汽车信息的id
* @return 汽车信息封装成的Map
*/
Map<String, Object> selectByIdReturnMap(Long id);
<!-- MyBatis 中 map 为 java.util.Map 的别名 -->
<select id="selectByIdReturnMap" resultType="map">
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>
@org.junit.Test
public void testSelectByIdReturnMap() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Map<String, Object> map = mapper.selectByIdReturnMap(5L);
System.out.println(map);
SqlSessionUtil.close();
}
List<Map>
List<Map>
相当于List<Car>
/**
* 查询所有的汽车信息
*
* @return 由汽车信息封装在Map中组成的List集合
*/
List<Map<String, Object>> selectAllReturnListMap();
<!-- MyBatis 中 map 为 java.util.Map 的别名 -->
<!-- 返回的还是 map,只有最后数据多个map封装在list中 -->
<select id="selectAllReturnListMap" resultType="map">
select id,
car_num as carNum,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from t_car;
</select>
@org.junit.Test
public void testSelectAllReturnListMap() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Map<String, Object>> maps = mapper.selectAllReturnListMap();
maps.forEach(System.out::println);
SqlSessionUtil.close();
}
Map<String, Map>
List<Map>
进行接收,不利于我们对结果集中的数据进行查询,需要顺序遍历查询/**
* 查询所有的汽车信息
*
* @return 由汽车信息封装在Map中组成的Map集合
*/
@MapKey("id") // @MapKey 注解的属性 value 指定使用数据哪个字段作为Map 的 key
// 作为大Map key 的数据类型要与@MapKey 注解指定的字段的数据类型一致
Map<Long, Map<String, Object>> selectAllReturnMapMap();
<!-- MyBatis 中 map 为 java.util.Map 的别名 -->
<!-- 返回的还是 map,只有最后数据多个map封装在Map中 -->
<select id="selectAllReturnMapMap" resultType="map">
select id,
car_num as carNum,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from t_car;
</select>
@org.junit.Test
public void testSelectAllReturnMapMap() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Map<Long, Map<String, Object>> map = mapper.selectAllReturnMapMap();
System.out.println(map.get(5L));
System.out.println(map.get(7L));
SqlSessionUtil.close();
}
/**
* 查询所有的汽车信息
* 使用 ResultMap 标签进行结果映射
*
* @return 汽车信息组成的集合
*/
List<Car> selectAllByResultMap();
<!-- 定义一个结果映射 在这个结果映射中指定数据库表字段和Java类的属性名之间的对应关系 type="":用于指定Java类的类名 id="":指定 resultMap 的唯一标识,这个id要在select标签中进行使用 --> <!-- 在Mybatis核心配置文件中指定了别名,所以可以直接使用car --> <resultMap id="carResultMap" type="car"> <!-- 如果数据库表中有主键,建议配一个id标签,提高Mybatis的执行效率 --> <!-- 当然 id 也可以使用 result 标签,就是效率较低 --> <id property="id" column="id"/> <!-- property="":填写Java类的属性名 column="":填写数据库表的字段名 javaType:指定Java类属性的类型,不用Mybatis自动类型推断,提高效率(可以使用别名) jdbcType:指定数据库表字段的类型,不用Mybatis自动类型推断,提高效率 --> <result property="carNum" column="car_num" javaType="java.lang.String" jdbcType="VARCHAR" /> <!-- 如果column和property的属性值一样可以省略 --> <!-- <result property="brand" column="brand"/> --> <result property="guidePrice" column="guide_price"/> <result property="produceTime" column="produce_time"/> <result property="carType" column="carType" javaType="string" jdbcType="VARCHAR" /> </resultMap> <!-- 使用结果映射,在select标签中不使用resultType 而是使用 resultMap,其属性值为 resultMap 的 id 使用 resultMap 指定结果映射 --> <select id="selectAllByResultMap" resultMap="carResultMap"> select * from t_car; </select>
@org.junit.Test
public void testSelectAllByResultMap() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAllByResultMap();
cars.forEach(System.out::println);
SqlSessionUtil.close();
}
驼峰命名自动映射这种方式的前提是:Java类的属性名遵循Java的命名规范,数据库表的列名遵循SQL的命名规范。
Java类的属性名与数据库表的列名的对应关系:
| 实体类中的属性名 | 数据库表的列名 |
| — | — |
| carNum | car_num |
| carType | car_type |
| produceTime | produce_time |
开启驼峰命名自动映射,在MyBatis的核心配置文件中的settings标签中进行配置
<settings>
<!-- 开启驼峰命名自动映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
/**
* 查询所有汽车信息
* 使用驼峰命名自动映射
*
* @return 汽车信息组成的集合
*/
List<Car> selectAllByMapUnderscoreToCamelCase();
<select id="selectAllByMapUnderscoreToCamelCase" resultType="car">
select * from t_car;
</select>
@org.junit.Test
public void testSelectAllByMapUnderscoreToCamelCase() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAllByMapUnderscoreToCamelCase();
cars.forEach(System.out::println);
SqlSessionUtil.close();
}
/**
* 查询汽车信息的总记录条数
*
* @return 汽车信息的总记录条数
*/
Long selectTotal();
<!-- long 是 java.util.Long 的别名 -->
<select id="selectTotal" resultType="long">
select count(*) from t_car;
</select>
@org.junit.Test
public void testSelectTotal() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Long total = mapper.selectTotal();
System.out.println(total);
SqlSessionUtil.close();
}
delete from t_car where id in(1,2,3,4,5,6,...);
,其中,SQL语句中的 in()
中的内容是动态的,根据用户选择的id不同,SQL语句 in()
中的内容是不同的select * from t_car where brand like '丰田%' and guide_price > 30 and .....;
,其中,SQL语句中where后面条件的个数是不确定的,是动态的,用户选择不同的条件,SQL语句中条件的个数不同/**
* 多条件查询
* 可能的条件包括:品牌(brand)、指导价格(guide_price)、汽车类型(car_type)
*
* @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"> <!-- where 1 = 1 保证条件都为空时sql语法可以通过 --> <!-- 如果条件都为空,并且没有1 = 1,则SQL语句会变为select * from t_car where, 会报语法错误 --> select * from t_car where 1 = 1 <!-- 1. if标签中test属性是必须的。 2. if标签中test属性的值是false或者true。 if标签的test属性中可以写表达式,值为false或者true 3. 如果test是true,则if标签中的sql语句就会拼接。反之,则不会拼接。 4. 在test属性中使用接口方法中参数的方式:(与#{}中使用接口方法中参数一样) 1)当使用了@Param注解,那么test中要出现的是@Param注解指定的参数名。 @Param("brand"),那么这里只能使用brand 2)当没有使用@Param注解,那么test中要出现的是: param1 param2 param3 arg0 arg1 arg2.... 3)当使用了POJO,那么test中出现的是POJO类的属性名。 5. 在mybatis的动态SQL当中,不能使用&&,只能使用and。 --> <!-- 第一个条件添加and防止“where 1 = 1 brand like "%"#{brand}"%"”语法错误 --> <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<Car> cars = mapper.selectByMultiCondition("比亚迪", 2.0, "新能源"); // 假设三个条件都是空 //List<Car> cars = mapper.selectByMultiCondition("", null, ""); // 假设后两个条件不为空,第一个条件为空 //List<Car> 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标签是专门负责where子句动态生成的。 如果where标签中的条件都不成立,则SQL语句中不会有where子句 --> <where> <!-- 可以自动去除条件前面多余的and或or --> <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<Car> cars = mapper.selectByMultiConditionWithWhere("比亚迪", 2.0, "新能源");
// 三个条件都是空
//List<Car> cars = mapper.selectByMultiConditionWithWhere("", null, "");
// 如果第一个条件是空
//List<Car> 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 <!-- prefix:加前缀 suffix:加后缀 prefixOverrides:删除前缀 suffixOverrides:删除后缀 --> <!-- prefix="where" 是在trim标签内容的前面添加 where, 这个添加是动态的 如果有条件,会动态添加where,没有则不添加 --> <!-- suffixOverrides="and|or" 把trim标签中动态判断完后的内容多余的后缀and或or去掉 --> <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>
/**
* 更新信息,使用set标签
* @param car
* @return
*/
int updateWithSet(Car car);
<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{
}
只有一个分支会被选择!!!!
需求:先根据品牌查询,如果没有提供品牌,再根据指导价格查询,如果没有提供指导价格,就根据汽车类型查询。
/**
* 使用choose when otherwise标签查询
* @param brand
* @param guidePrice
* @param produceTime
* @return
*/
List<Car> selectWithChoose(@Param("brand") String brand, @Param("guidePrice") Double guidePrice, @Param("carType") String carType);
<select id="selectByChoose" resultType="Car"> select * from t_car <where> <!-- 先根据品牌查询,没有传品牌,在根据价格查询, 都没有最后根据汽车类型查询 --> <choose> <!--只有一个分支会执行,所以不需要and or关键字--> <when test="brand != null and brand != ''"> brand like "%"#{brand}"%" </when> <when test="guidePrice != null and guidePrice != ''"> guide_price > #{guidePrice} </when> <!--都为null,还是执行这个分支,car_type = null,只是查不到数据--> <otherwise> car_type = #{carType} </otherwise> </choose> </where> </select>
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);
<!-- foreach标签的属性: collection:指定数组或者集合 item:代表数组或集合中的元素 separator:循环之间的分隔符,自动添加分隔符,会自动在分隔符的两边添加空格 open: foreach循环拼接的所有sql语句的最前面以什么开始。 close: foreach循环拼接的所有sql语句的最后面以什么结束。 --> <delete id="deleteByIds"> <!-- delete from t_car where id in( <foreach collection="ids" item="aaaaaaa" separator=","> #{aaaaaaa} </foreach> ) 没有使用@Param注解,上面的SQL报错了,错误信息是:[array, arg0] 因为没有使用@Param注解时,参数在map集合中存储,默认使用array, arg0作为key map.put("array", 数组); map.put("arg0", 数组); --> delete from t_car where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete>
/**
* 根据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>
/**
* 批量插入,一次插入多条Car信息
* @param cars
* @return
*/
int insertBatch(@Param("cars") List<Car> cars);
<insert id="insertBatch">
insert into t_car values
<!-- cars 汽车信息组成的list集合,car一个汽车类的对象,取属性,对象.属性 -->
<foreach collection="cars" item="car" separator=",">
(null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
</foreach>
</insert>
<!-- 声明sql片段 将重复的SQL片段提取出来 --> <sql id="carCols"> id, car_num carNum, brand, guide_price guidePrice, produce_time produceTime, car_type carType </sql> <select id="selectAllRetMap" resultType="map"> <!-- 将声明的sql片段包含进来 --> 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>
use mybatis_study; drop table t_clazz; create table t_clazz( cid int primary key, cname varchar(255) ); drop table t_student; create table t_student( sid int primary key, sname varchar(255), cid int ); insert into t_clazz values (1000, '高三一班'), (1001, '高三二班'); insert into t_student values (1, '张三', 1000), (2, '李四', 1000), (3, '王五', 1000), (4, '赵六', 1001), (5, '钱七', 1001);
<dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.5</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies>
package cw.study.mybatis.pojo; /** * ClassName: Clazz * Package: cw.study.mybatis.pojo * Description: * 班级信息 * * @Author tcw * @Create 2023-06-04 11:38 * @Version 1.0 */ public class Clazz { private Integer cid; private String cname; public Clazz() { } public Clazz(Integer cid, String cname) { this.cid = cid; this.cname = cname; } @Override public String toString() { return "Clazz{" + "cid=" + cid + ", cname='" + cname + '\'' + '}'; } public Integer getCid() { return cid; } public void setCid(Integer cid) { this.cid = cid; } public String getCname() { return cname; } public void setCname(String cname) { this.cname = cname; } }
package cw.study.mybatis.pojo; /** * ClassName: Student * Package: cw.study.mybatis.pojo * Description: * 学生信息 * * @Author tcw * @Create 2023-06-04 11:38 * @Version 1.0 */ public class Student { private Integer sid; private String sname; public Student() { } public Student(Integer sid, String sname) { this.sid = sid; this.sname = sname; } @Override public String toString() { return "Student{" + "sid=" + sid + ", sname='" + sname + '\'' + ", " + '}'; } public Integer getSid() { return sid; } public void setSid(Integer sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } }
/**
* 学生信息
*/
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);
<!--多对一映射的第一种方式:一条SQL语句,级联属性映射。--> <resultMap id="studentResultMap" type="student"> <!-- column 查询结果的列名,赋值到 property 对象的属性上 --> <id property="sid" column="sid"/> <result property="sname" column="sname"/> <!-- 为clazz(班级对象)的属性赋值 --> <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_student s left join t_clazz c on s.cid = c.cid where s.sid = #{id}; </select>
@org.junit.Test
public void test01() {
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.selectById(1);
System.out.println(student);
}
/**
* 一条SQL语句,association
* @param id
* @return
*/
Student selectByIdAssociation(Integer id);
<!-- type 查询结果映射到类型 --> <resultMap id="studentResultMapAssociation" type="Student"> <!-- column 查询结果的列名,赋值到 property 对象的属性上 --> <id property="sid" column="sid"/> <result property="sname" column="sname"/> <!-- association:翻译为关联。一个Student对象关联一个Clazz对象 property:提供要映射的POJO类的属性名。关联对象引用的属性名 javaType:用来指定要映射的java类型。 --> <association property="clazz" javaType="Clazz"> <!-- column 查询结果的列名,赋值到 property 对象的属性上 --> <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_student s left join t_clazz c on s.cid = c.cid where s.sid = #{id}; </select>
@org.junit.Test
public void test02() {
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.selectByIdAssociation(4);
System.out.println(student);
}
/**
* 分部查询第一步:先根据学生的sid查询学生的信息。
* @param sid
* @return
*/
Student selectByIdStep1(Integer sid);
/**
* 分步查询第二步:根据cid获取班级信息。
* @param cid
* @return
*/
Clazz selectByIdStep2(Integer cid);
<!--分步查询第二步:根据cid获取班级信息。-->
<select id="selectByIdStep2" resultType="Clazz">
select cid, cname
from t_clazz
where cid = #{cid};
</select>
<!-- 两条SQL语句,完成多对一的分步查询 --> <!-- 第一步:根据学生的id查询学生的所有信息。这些信息当中含有班级id(cid) --> <select id="selectByIdStep1" resultMap="studentResultMapByStep"> select sid, sname, cid as clazzId from t_student where sid = #{sid}; </select> <resultMap id="studentResultMapByStep" type="Student"> <id property="sid" column="sid"/> <result property="sname" column="sname"/> <!-- 第二步:根据班级的id查询班级信息 第一步中查询出来的cid(有起别名使用别名)通过column标签属性 传给cw.study.mybatis.mapper.ClazzMapper.selectByIdStep2对应的SQL语句 进行第二步查询,根据班级的id查询班级信息 --> <association property="clazz" select="cw.study.mybatis.mapper.ClazzMapper.selectByIdStep2" column="clazzId" ></association> </resultMap>
@org.junit.Test
public void test03() {
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.selectByIdStep1(3);
System.out.println(student);
}
<resultMap id="studentResultMapByStep" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<!-- 使用fetchType="lazy"开启局部懒加载 -->
<!-- fetchType="eager"不开启懒加载 -->
<association
property="clazz"
select="cw.study.mybatis.mapper.ClazzMapper.selectByIdStep2"
column="clazzId"
fetchType="lazy"
></association>
</resultMap>
@org.junit.Test
public void test03() {
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.selectByIdStep1(3);
// 没有使用到第二条SQL语句查询的信息,不会执行第二条SQL
System.out.println(student.getSid());
System.out.println(student.getSname());
// 需要使用第二条SQL查询的信息时,才会执行
System.out.println(student.getClazz());
}
<settings>
<!--延迟加载的全局开关。默认值false不开启。-->
<!--什么意思:所有只要但凡带有分步的,都采用延迟加载。-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<resultMap id="studentResultMapByStep" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association
property="clazz"
select="cw.study.mybatis.mapper.ClazzMapper.selectByIdStep2"
column="clazzId"
></association>
</resultMap>
List<Student> stus;
属性。/**
* 班级信息
*/
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);
<select id="selectByCollection" resultMap="clazzResultMap"> select c.cid, c.cname, s.sid, s.sname from t_clazz c left join t_student s on c.cid = s.cid where c.cid = #{cid}; </select> <resultMap id="clazzResultMap" type="Clazz"> <id property="cid" column="cid"/> <result property="cname" column="cname"/> <!-- 一对多,使用collection ofType:指定集合中的元素的类型 --> <!-- 不能为Student对象的clazz属性赋值, 否则会死循环Student中有clazz,clazz中有Student --> <collection property="stus" ofType="Student"> <id property="sid" column="sid"/> <result property="sname" column="sname"/> </collection> </resultMap>
@org.junit.Test
public void test04() {
SqlSession sqlSession = SqlSessionUtil.openSession();
ClazzMapper clazzMapper = sqlSession.getMapper(ClazzMapper.class);
Clazz clazz = clazzMapper.selectByCollection(1000);
System.out.println(clazz.getCid() + " " + clazz.getCname());
clazz.getStus().forEach(System.out::println);
}
/**
* 分步查询。第一步:根据班级编号获取班级信息。
* @param cid 班级编号
* @return
*/
Clazz selectByStep1(Integer cid);
/**
* 根据班级编号查询学生信息。
* @param cid
* @return
*/
List<Student> selectByCidStep2(Integer cid);
<!-- 根据班级编号查询学生信息。 -->
<select id="selectByCidStep2" resultType="Student">
select sid, sname from t_student where cid = #{cid}
</select>
<!--分步查询第一步:根据班级的cid获取班级信息。--> <select id="selectByStep1" resultMap="clazzResultMapStep"> select cid, cname from t_clazz where cid = #{cid}; </select> <resultMap id="clazzResultMapStep" type="Clazz"> <id property="cid" column="cid"/> <result property="cname" column="cname"/> <!-- 第二步:根据班级的id查询学生信息 第一步中查询出来的cid(有起别名使用别名)通过column标签属性 传给cw.study.mybatis.mapper.StudentMapper.selectByCidStep2对应的SQL语句 进行第二步查询,根据班级的id查询学生信息 --> <association property="stus" select="cw.study.mybatis.mapper.StudentMapper.selectByCidStep2" column="cid" /> </resultMap>
@org.junit.Test
public void test05() {
SqlSession sqlSession = SqlSessionUtil.openSession();
ClazzMapper clazzMapper = sqlSession.getMapper(ClazzMapper.class);
Clazz clazz = clazzMapper.selectByStep1(1001);
System.out.println(clazz.getCid() + " " + clazz.getCname());
clazz.getStus().forEach(System.out::println);
}
<resultMap id="clazzResultMapStep" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<association
property="stus"
select="cw.study.mybatis.mapper.StudentMapper.selectByCidStep2"
column="cid"
fetchType="lazy"
/>
</resultMap>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
/**
* 根据汽车id查询汽车信息
* @param id
* @return
*/
Car selectById(Long id);
<select id="selectById" resultType="Car">
select * from t_car where id = #{id};
</select>
@org.junit.Test
public void test01() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper carMapper = sqlSession.getMapper(CarMapper.class);
Car car1 = carMapper.selectById(5L);
System.out.println(car1);
Car car2 = carMapper.selectById(5L);
System.out.println(car2);
SqlSessionUtil.close();
}
@org.junit.Test
public void test02() throws IOException {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
CarMapper carMapper1 = sqlSession1.getMapper(CarMapper.class);
System.out.println(carMapper1.selectById(5L));
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper carMapper2 = sqlSession2.getMapper(CarMapper.class);
System.out.println(carMapper2.selectById(5L));
sqlSession2.close();
}
sqlSession.clearCache();
<setting name="cacheEnabled" value="true">
全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认就是true,无需设置。
<cache />
<!--
默认情况下,二级缓存机制是开启的。
只需要在对应的SqlMapper.xml文件中添加以下标签。
表示要在该SqlMapper.xml文件中使用该二级缓存。
-->
<cache/>
public class Car implements Serializable {}
@org.junit.Test public void test02() throws IOException { // 这里只有一个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.selectById(5L); System.out.println(car1); // 如果执行了这行代码,sqlSession1的一级缓存中的数据会放到二级缓存当中。 sqlSession1.close(); // 由于当前执行的SQL和查询条件与二级缓存中的已有的缓存数据一样 // 直接从二级缓存中获取,不再执行SQL语句进行查询 Car car2 = mapper2.selectById(5L); System.out.println(car2); // 程序执行到这里的时候,会将sqlSession2这个一级缓存中的数据写入到二级缓存当中。 sqlSession2.close(); }
<!--mybatis集成ehcache的组件-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!--磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存--> <diskStore path="e:/ehcache"/> <!--defaultCache:默认的管理策略--> <!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断--> <!--maxElementsInMemory:在内存中缓存的element的最大数目--> <!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上--> <!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false--> <!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问--> <!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问--> <!--memoryStoreEvictionPolicy:缓存的3 种清空策略--> <!--FIFO:first in first out (先进先出)--> <!--LFU:Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存--> <!--LRU:Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存--> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/> </ehcache>
<!-- <cache/> 表示使用mybatis自己的缓存 -->
<!-- 集成使用第三方EhCache缓存 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
@org.junit.Test public void test02() throws IOException { // 这里只有一个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.selectById(5L); System.out.println(car1); // 如果执行了这行代码,sqlSession1的一级缓存中的数据会放到二级缓存当中。 sqlSession1.close(); // 由于当前执行的SQL和查询条件与二级缓存中的已有的缓存数据一样 // 直接从二级缓存中获取,不再执行SQL语句进行查询 Car car2 = mapper2.selectById(5L); System.out.println(car2); // 程序执行到这里的时候,会将sqlSession2这个一级缓存中的数据写入到二级缓存当中。 sqlSession2.close(); }
<!--定制构建过程--> <build> <!--可配置多个插件--> <plugins> <!--其中的一个插件:mybatis逆向工程插件--> <plugin> <!--插件的GAV坐标--> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.4.1</version> <!-- 允许覆盖 会将原先生成的删除,然后覆盖原先的重新生成 --> <configuration> <overwrite>true</overwrite> </configuration> <!--插件的依赖--> <dependencies> <!-- mysql驱动依赖 根据数据库表动态生成,需要连接数据库 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> </dependencies> </plugin> </plugins> </build>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!-- targetRuntime有两个值: MyBatis3Simple:生成的是基础版,只有基本的增删改查。 MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。 --> <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> <!-- 生成pojo包名和位置 --> <javaModelGenerator targetPackage="com.powernode.mybatis.pojo" targetProject="src/main/java"> <!--是否开启子包--> <!--如果没有开启子包,com.powernode.mybatis.pojo整个会被作为一个文件夹名--> <property name="enableSubPackages" value="true"/> <!--是否去除字段名的前后空白--> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- 生成SQL映射文件的包名和位置 --> <sqlMapGenerator targetPackage="com.powernode.mybatis.mapper" targetProject="src/main/resources"> <!--是否开启子包--> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <!-- 生成Mapper接口的包名和位置 --> <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>
<!--
targetRuntime有两个值:
MyBatis3Simple:生成的是基础版,只有基本的增删改查。
MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
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语句。 // QBC 面向对象查询 // 封装条件,通过CarExample对象来封装查询条件 CarExample carExample = new CarExample(); // 调用carExample.createCriteria()方法来创建查询条件 // 继续向后调用方法追加查询条件 carExample.createCriteria() .andBrandLike("帕萨特") // 模糊查询 .andGuidePriceGreaterThan(new BigDecimal(20.0)); // 并且价格大于20 // 添加or // 上面添加的为and条件,or()开始添加or条件 // (... and ... 前面的查询条件) or (...) 生成后的查询条件 carExample.or().andCarTypeEqualTo("燃油车"); // 执行查询 // 更加封装的条件进行查询 List<Car> cars2 = mapper.selectByExample(carExample); cars2.forEach(car2 -> System.out.println(car2)); sqlSession.close(); } }
limit 开始下标, 显示的记录条数
limit 显示的记录条数
,相当于limit 0, 显示的记录条数
,即从第一条记录开始显示指定数目的记录条数select
*
from
tableName ......
limit
(pageNum - 1) * pageSize, pageSize
/**
* 分页查询
* @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();
}
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 配置mybatis分页拦截器插件,使用PageHelper插件的分页拦截器插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
/**
* 查询所有的Car,通过分页查询插件PageHelper完成。
* @return
*/
List<Car> selectAll();
<!-- 使用了PageHelper插件,分页不用我们进行处理,直接查询数据即可 -->
<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);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。