全网最全老杜Mybatis笔记
ORM(MyBatis属于半自动化ORM框架)
什么是ORM?
像User这样的类,有特殊的称呼:
有的人把它叫做: pojo(普通java类)有的叫做: bean(豆子)
有的叫做: domain(领域模型)
环境准备:
考虑到mybatis学习要用到maven
所以来复习一下Maven是怎么配置的!
Maven
1.先下载Maven
Maven – Welcome to Apache Maven
2.修改配置镜像
搜索镜像 maven.aliyun.com
把之前的文件内容注释,添加阿里镜像
3.新建文件夹仓库
4.配置idea
-DarchetypeCatalog=internal
5.重启idea
第一个mybatis程序
开发我的第一个MyBatis程序
1.resources目录:
放在这个目录当中的,一般都是资源文件,配置文件。
直接放到resources目录下的资源,等同于放到了类的根路径下。
2.开发步骤(前两步在pom.xml里)
第一步:打包方式 jar
<packaging>jar</packaging>
第二步:导入依赖
Maven的远程仓库
https://mvnrepository.com
搜索mybatis,搜索mysql
如下:
<dependencies>
<!-- mysql -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.31</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
</dependencies>
第三步:编写mybatis核心配置文件:
mybatis-config.xml
注意:
第一这个文件名不是必须叫做mybatis-config.xml,可以用其他的名字。只是大家都采用这个名字。
第二:这个文件存放的位置也不是固定的,可以随意,但一般情况下,会放到类的根路径下。
怎么配置呢?
我们从mybatis中文网
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
其他的代码看不懂,我们应该能看懂(显然是连接数据库的)
来配置一下:
<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"/>
第四步:编写XxxxMapper.xml文件
在这个配置文件当中编写SQL语句。
这个文件名也不是固定的,放的位置也不是固定,我们这里给它起个名字,叫做:CarMapper.xml把它暂时放到类的根路径下。
配置也是从mybatis中文网复制:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
写一个插入语句吧:
<insert id="">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values (null,'1003','丰田',30,'2003-08-09','燃油车')
</insert>
第五步:在mybatis-config.xml文件中指定XxxxMapper.xml文件的路径:
<mapper resource="CarMapper. xml"/>
注意:resource属性会自动从类的根路径下开始查找资源。
而CarMapper. xml刚好在根目录下。
第六步:编写mybatis程序
现在我们正式进入mybatis学习
先创建软件包
创建第一个java:
package com.powenode.mybatis.test;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
public class day1 {
public static void main(String[] args) throws Exception{
//获取sqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = sqlSessionFactoryBuilder.build(is);
//获取SqlSession对象
SqlSession sqlSession = build.openSession();
//执行sql语句
int count = sqlSession.insert("insertCar");//执行影响的行数
System.out.println(count);
//提交
sqlSession.commit();
}
}
成功了!!!!
第一个比较完整的Mybatis程序
package com.powenode.mybatis.test;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
public class mybatisComplete {
public static void main(String[] args) {
SqlSession sqlSession=null;
try {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
sqlSession = sqlSessionFactory.openSession();
//执行sql语句
int count = sqlSession.insert("insertCar");
System.out.println(count);
//处理到这没有发现异常,提交事务,终止事务
sqlSession.commit();
} catch (Exception e) {
if (sqlSession!=null){
sqlSession.rollback();
}
e.printStackTrace();
}finally {
if (sqlSession != null){
sqlSession.close();
}
}
}
}
和上面的程序差不多,就是更加完整了。。。
junit复习
新建一个模块进行复习
导入依赖:
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
开始操作:
规范:
单元测试方法写多少个(一般是一个业务方法对应一个测试方式)
测试方法的规范:public void testXxxx(){}
测试方法的方法名:以test开始。假设测试的方法是sum,这个测试方法名:testSum
@Test注解非常重要,被这个注解标注的方法就是一个单元测试方法。
这俩个里的代码:
package test;
public class mathService {
public int sum(int a,int b){
return a+b;
}
}
测试:
package text;
import org.junit.Assert;
import org.junit.Test;
import test.mathService;
public class mathServiceTest {
@Test
public void testSum(){
mathService ms = new mathService();
int actual = ms.sum(3, 1);
int espected = 4;
Assert.assertEquals(espected,actual);
}
}
我们单纯的输出信息的话,不便于调试,所以有日志;
Mybatis集成日志框架
我们来实现第三方的日志组件-------------->
集成logback日志框架:
第一步:添加框架支持
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
<scope>test</scope>
</dependency>
第二步:引入logback的xml配置文件
这个配置文件的名字必须叫做: logback.xml或者logback-test.xml,不能是其它的名字。
这个配置文件必须放到类的根路径下。不能是其他位置。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProperty scope="context" name="logName" source="logging.file.name" defaultValue="log.log" />
<!--定义日志文件的存储地址-->
<property name="LOG_HOME" value="${logName}" />
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 输出到日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}.%d{yyyy-MM-dd}.%i</FileNamePattern>
<MaxHistory>30</MaxHistory>
<MaxFileSize>50MB</MaxFileSize>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 自定义logger -->
<logger name="com.dispart" level="debug" additivity="false">
<appender-ref ref="console" />
</logger>
<!--sql语句执行输出-->
<logger name="org.apache.ibatis" level="debug" additivity="false">
<appender-ref ref="console" />
</logger>
<root level="info" additivity="false">
<appender-ref ref="console" />
</root>
</configuration>
用junit测试:
每一次获取sqlSession对象太过于繁琐,我们可以自己封装一个工具类:
Mybatis工具类SqlSessionUtil的封装
封装类如下:
package mybatis.util;
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;
public class SqlSessionUtil {
//工具类的构造方法一般都是私有化的。
//工具类中所有的方法都是静态的,直接采用类名即可调用。不需要new对象。
// 为了防止new对象,构造方法私有化。
private SqlSessionUtil(){}
private static SqlSessionFactory build;
//类加载时运行
// SqlSessionUtil工具类在进行第一次加载的时候,解析mybatis-config.xml文件。创建SqLSessionFactory对象。
static {
try {
build = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSession openSession(){
return build.openSession();
}
}
现在用junit去测试一下:
@Test
public void utilTest(){
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.insert("insertCar");
System.out.println(count);
sqlSession.commit();
}
没有问题!
使用Mybatis完成CRUD
C:create
R:retrieve
U:update
D:delete
就是数据库的基本操作,增删改查;
mybatis完成insert,利用pojo类传参;
创建car的pojo类;
package pojo;
public class car {
//数据库表当中的字段应该和pojo类的属性一一对应。
// 建议使用包装类,这样可以防止null的问题。
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String 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;
}
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;
}
}
修改car的xml文件(#{pojo类的元素})
<insert id="insertCar">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values (#{id},#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType});
</insert>
测试:
public void test(){
car car = new car(null, "3333", "比亚迪", 30.00, "2003-09-03", "电车");
SqlSession sqlSession = SqlSessionUtil.openSession();
sqlSession.insert("insertCar",car);
sqlSession.commit();
sqlSession.close();
}
mybatis完成delete
这个就比较简单了,因为只要一个元素,id去删除
我们先在car的xml文件里去加一个删除的操作;
<delete id="deleteById">
delete from t_car where id = #{id};
</delete>
然后进行操作:
public void test_1(){
SqlSession sqlSession = SqlSessionUtil.openSession();
sqlSession.delete("deleteById",7);
sqlSession.commit();
sqlSession.close();
}
mybatis完成update
这个和上面的insert有点相似;
我们先在car的xml文件里去加一个更新的操作;
public void test_2(){
car car = new car(6L, "3333", "比亚迪", 30.00, "2003-09-03", "电车");
SqlSession sqlSession = SqlSessionUtil.openSession();
sqlSession.delete("updateById",car);
sqlSession.commit();
sqlSession.close();
}
mybatis完成select查询
完成一次查询
car的xml文件:
<select id="selectById" resultType="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>注:
1.resultType="pojo.car"这个是指定类的类型为car
2.为什么不写 select * from t_car where id = #{id}呢?因为这样可能有些结果是null,表的列名要和pojo类的类名一样才可以查出结果,所以要用别名;
测试代码:
public void test_3(){
SqlSession sqlSession = SqlSessionUtil.openSession();
Object car = sqlSession.selectOne("selectById", 1);
System.out.println(car);
sqlSession.commit();
sqlSession.close();
}
select查询所有;
老样子:
<select id="selectAll" resultType="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>
测试:
public void test_4(){
SqlSession sqlSession = SqlSessionUtil.openSession();
List<Object> all = sqlSession.selectList("selectAll");
all.forEach(car-> System.out.println(car));
sqlSession.commit();
sqlSession.close();
}
namespace的作用
命名空间,如果有操作的id有相同的话,前面要加上namespace;
就比如再写一个xml文件,执行select操作,id与之前的xml文件相同的话
核心配置文件
多环境
当要多个数据库使用的时候,我们来配置;
使用:
事务管理器
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
<transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager>
数据源(dataSource)
如:
<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>
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
来看pooled:
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:
- poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
- poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
- poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
- poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
- poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和。 默认值:3(新增于 3.4.5)
- poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
- poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
- poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
属性(properties)
这些属性可以在外部进行配置,并可以进行动态替换。
这时候可以在根目录下创建一个jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=root
然后再在核心配置文件的去引入;
<properties resource="jdbc.properties"></properties>
然后再获取:用${}
<environment id="development">
<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>
在web中应用mybatis
先建立一张数据库的表:
插入数据:
创建一个新模块:
添加依赖:
先创建包:com.powernode里
编辑account类:
package com.powernode.pojo;
public class account {
private Long id;
private String actno;
private Double 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;
}
}
再加一个之前写的工具类:
再配置根目录:
写前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>银行转账系统</title>
</head>
<body>
<form action="/transfer" method="post">
转出账号:<input name="from" type="text"><br>
转入账号:<input name="to" type="text"><br>
转账金额:<input name="money" type="text">
<input type="submit" value="提交">
</form>
</body>
</html>
接下来不想写了。。。。。
mybatis事务的控制:
上面的web应用的一部分:
业务层:
package com.powernode.service.imp;
import com.powernode.dao.accountD;
import com.powernode.dao.imp.accountDImp;
import com.powernode.exceptions.noMoneyException;
import com.powernode.exceptions.transferException;
import com.powernode.pojo.account;
import com.powernode.service.accService;
public class accServiceImp implements accService {
private accountD us= new accountDImp();
public void transfer(String from,String to,double money) throws noMoneyException, transferException {
//查询是否钱够用
double balance = us.selectA(from).getBalance();
double balance2 = us.selectA(to).getBalance();
if (balance>=money){
//钱足够的话
double t1 = balance - money;
double t2 = balance2+ money;
//封装pojo类
account ac1 = new account();
ac1.setActno(from);
System.out.println(t1);
ac1.setBalance(t1);
account ac2 = new account();
ac2.setActno(to);
ac2.setBalance(t2);
//更新
int i = us.updateA(ac1);
//在这发生异常的话,会导致钱减少了,但是没转账成功
int i1 = us.updateA(ac2);
if(i+i1!=2){
throw new transferException("转账失败");
}
}
else{
throw new noMoneyException("余额不够,请充值");
}
}
}
我们怎么解决这个问题呢?
应该在这个业务层去创建SqlSession对象,并且提交;
所以要修改一下代码:
数据库控制层把commit等代码删掉;
修改工具类:
public static SqlSession openSession(){
SqlSession sqlSession = local.get();
if(sqlSession==null){
sqlSession=build.openSession();
//将sqlSession绑定到当前线程上
local.set(sqlSession);
}
return sqlSession;
}
public static void Close(SqlSession sqlSession){
sqlSession.close();
//从当前线程移除sqlSession对象
local.remove();
}
业务层:
public void transfer(String from,String to,double money) throws noMoneyException, transferException {
SqlSession sqlSession = SqlSessionUtil.openSession();
//查询是否钱够用
double balance = us.selectA(from).getBalance();
double balance2 = us.selectA(to).getBalance();
if (balance>=money){
//钱足够的话
double t1 = balance - money;
double t2 = balance2+ money;
//封装pojo类
account ac1 = new account();
ac1.setActno(from);
System.out.println(t1);
ac1.setBalance(t1);
account ac2 = new account();
ac2.setActno(to);
ac2.setBalance(t2);
//更新
int i = us.updateA(ac1);
int i1 = us.updateA(ac2);
if(i+i1!=2){
throw new transferException("转账失败");
}
}
else{
throw new noMoneyException("余额不够,请充值");
}
sqlSession.commit();
SqlSessionUtil.closeS(sqlSession);
}
javassist动态生成类
pom.xml文件里写好依赖
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.0-GA</version>
</dependency>
</dependencies>
来写:
package javassist;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
public class javassistTest {
@Test
public void test() throws Exception {
//获取类池,这个是生成class类的
ClassPool pool = ClassPool.getDefault();
//制造类,需要知道类的名称
CtClass ctClass = pool.makeClass("com.powernode.dao.imp.accountDImp");
//制造方法
String methodCode = "public void update(){System.out.print(222);}";
CtMethod method = CtMethod.make(methodCode, ctClass);
//将方法添加到类中
ctClass.addMethod(method);
//在内存中生成class
ctClass.toClass();
//类加载到jvm中,返回类的字节码文件
Class<?> aClass = Class.forName("com.powernode.dao.imp.accountDImp");
//创建对象
Object o = aClass.newInstance();
//获取类中的方法
Method update = aClass.getDeclaredMethod("update");
//调用方法
update.invoke(o);
}
}
。。。。。。这个太繁琐,我们直接跳过
MyBatis的getMapper()方法
动态生成接口的实现类
注意:namespace不能随便写了,要写接口的路径
还有操作的id要和接口的方法名一样
使用
private accountD us = SqlSessionUtil.openSession().getMapper(accountD.class);
面向接口的方式进行CRUD
import mapper.carD;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import pojo.car;
import util.SqlSessionUtil;
import java.util.List;
public class test {
@Test
public void update(){
SqlSession sqlSession = SqlSessionUtil.openSession();
carD mapper = sqlSession.getMapper(carD.class);
car car = new car(6L, "4444", "比亚迪", 30.00, "2003-09-03", "电车");
mapper.updateById(car);
sqlSession.commit();
}
@Test
public void delete(){
SqlSession sqlSession = SqlSessionUtil.openSession();
carD mapper = sqlSession.getMapper(carD.class);
mapper.deleteById(6L);
sqlSession.commit();
}
@Test
public void insert(){
SqlSession sqlSession = SqlSessionUtil.openSession();
carD mapper = sqlSession.getMapper(carD.class);
car car = new car(6L, "4444", "比亚迪", 30.00, "2003-09-03", "电车");
mapper.insertCar(car);
sqlSession.commit();
}
@Test
public void select(){
SqlSession sqlSession = SqlSessionUtil.openSession();
carD mapper = sqlSession.getMapper(carD.class);
car car = mapper.selectById(6L);
System.out.println(car);
sqlSession.commit();
}
@Test
public void selectA(){
SqlSession sqlSession = SqlSessionUtil.openSession();
carD mapper = sqlSession.getMapper(carD.class);
List<car> cars = mapper.selectAll();
cars.forEach(car -> System.out.println(car));
sqlSession.commit();
}
}
#{}和${}的区别究竟是什么呢?
如果需要SQL语句的关键字放到SQL语句中,只能使用${},因为#{}是以值的形式放到SQL语句当中的。
#{}先编译再放值进去
${}先进行字符串拼接再编译,有sql注入的风险
mybatis小技巧
-批量删除
carMapper.xml
<delete id="plDelete">
delete from t_car where id in (${id});
</delete>
接口里:
public void plDelete(String id);
测试:
@Test
public void plDeleteTest(){
SqlSession sqlSession = SqlSessionUtil.openSession();
carD mapper = sqlSession.getMapper(carD.class);
mapper.plDelete("5,6");
sqlSession.commit();
}
--模糊查询
有两种方法可以用,第一个方法用${}
别名机制
mapper小技巧
使用自动生成的主键值
用map集合去插入数据
三剑客
@Test
public void insertByMapTest(){
SqlSession sqlSession = SqlSessionUtil.openSession();
carD mapper = sqlSession.getMapper(carD.class);
HashMap<String, Object> map = new HashMap<>();
map.put("id",16L);
map.put("carNum","5555");
map.put("brand","小米");
map.put("guidePrice",30);
map.put("produceTime","1999-08-07");
map.put("carType","油车");
mapper.insertByMap(map);
sqlSession.commit();
sqlSession.close();
}
<insert id="insertByMap">
insert into t_car values (#{id},#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType});
</insert>
public void insertByMap(Map map);
mybatis之多参数
我们之前写的都是单个参数,现在如果我们想完成一个select操作用2个参数去搜索,该怎么去实现呢?
mybatis会为我们自动封装一个map集合,我们用该方法去取值:
我们来试试:
<select id="selectByTwo" resultType="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=#{param1} and car_type = #{param2};
</select>
public void selectByTwoTest(){
SqlSession sqlSession = SqlSessionUtil.openSession();
carD mapper = sqlSession.getMapper(carD.class);
List<car> cars = mapper.selectByTwo("比亚迪","电车");
cars.forEach(car -> System.out.println(car));
sqlSession.commit();
sqlSession.close();
}
public List<car> selectByTwo(String b,String t);
param注解
public List<car> selectByTwo(@Param("brand") String b, @Param("car_type") String t);
Mybatis查询专题之返回map集合
返回一个map
<select id="selectToMap" 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>
public Map<String, Objects> selectToMap(Long id);
@Test
public void selectToMapTest(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Map<String, Objects> stringObjectsMap = mapper.selectToMap(11L);
System.out.println(stringObjectsMap);
sqlSession.commit();
sqlSession.close();
}
返回多个map
唯一要注意的点,不是list是map
返回一个大map
大map的key是id
@MapKey("id")
public Map<Long,Map<String,Objects>> selectToBigMap();
Mybatis结果映射
之前写select语句的时候,要求取别名,不然会出现null的现象。
很是不爽,我们可以设置一个结果映射。如下:
●驼峰自动映射
要符合:
开启:
查询返回数字
<select id="countAll" resultType="long">
select count(*) from t_car;
</select>
public Long countAll();
动态sql
if标签
<select id="ifOfSelect" 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 1=1
<if test="price != null and price != ''">
and guide_price >= #{price}
</if>
<if test="brand != null and brand != ''">
and brand like "%"#{brand}"%"
</if>
<if test="type != null and type != ''">
and car_type = #{type}
</if>
</select>
public List<Car> ifOfSelect(@Param("brand") String b,@Param("price") Double p,@Param("type") String t);
where标签
不需要写1=1了,我们直接把上面写的if放到where里就行,and(or)写在前面的话,会自己消除.
trim标签
<trim prefix="where" suffixOverrides="and|or">
<if test="price != null and price != ''">
guide_price >= #{price} and
</if>
<if test="brand != null and brand != ''">
brand like "%"#{brand}"%" and
</if>
<if test="type != null and type != ''">
car_type = #{type}
</if>
</trim>
set标签
choose标签
必然有一个条件会执行,全为null,最后一个也会执行;
foreach批量删除
<delete id="deleteOfForeach">
delete from t_car where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
public void deleteOfForeach(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Long[] its={14L,15L,16L};
mapper.deleteOfForeach(its);
sqlSession.commit();
sqlSession.close();
}
public void deleteOfForeach(@Param("ids") Long[] ids);
foreach批量添加
<insert id="insertMore">
insert into t_car values
<foreach collection="cars" item="car" separator=",">
(#{car.id},#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
</foreach>
</insert>
public void insertMore(@Param("cars") List<Car> cars);
@Test
public void insertMore(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = new ArrayList<>();
Car car1 = new Car(null, "4444", "比亚迪", 30.00, "2003-09-03", "电车");
Car car2 = new Car(null, "4444", "比亚迪", 30.00, "2003-09-03", "电车");
Car car3 = new Car(null, "4444", "比亚迪", 30.00, "2003-09-03", "电车");
cars.add(car1);
cars.add(car2);
cars.add(car3);
System.out.println(car1.getId());
mapper.insertMore(cars);
sqlSession.commit();
sqlSession.close();
}
多对一映射
mysql基础
多表联查--01---LEFT JOIN 实现多表联查_多表left join-CSDN博客
准备2张表
t_stu
t_class
按之前准备环境:
2张表2个mapper和pojo
2个xml文件
第一种方法,用sql的查询语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="ljy.mapper.StudentMapper">
<resultMap id="StudentMap" type="Student">
<id property="sid" column="sid"/>
<result property="sName" column="name"/>
<result property="c.cid" column="cid"/>
<result property="c.classN" column="classN"/>
</resultMap>
<select id="OneSelect" resultMap="StudentMap">
select s.sid,s.name,c.cid,c.classN
from t_stu s left join t_class c on s.cid=c.cid
where s.sid = #{sid}
</select>
</mapper>
第二种方式:
和第一种方式差不多
<resultMap id="selectByIdAssociation" type="Student">
<id property="sid" column="sid"/>
<result property="sName" column="name"/>
<association property="c" javaType="CLASS">
<id property="cid" column="cid"/>
<result property="classN" column="classN"/>
</association>
</resultMap>
<select id="selectByAssociation" resultMap="selectByIdAssociation">
select s.sid,s.name,c.cid,c.classN
from t_stu s left join t_class c on s.cid=c.cid
where s.sid = #{sid}
</select>
第三种方式:分布查询
先查学生的班级cid,再根据cid查班级
<resultMap id="selectByIdMoreStep" type="Student">
<id property="sid" column="sid"/>
<result property="sName" column="name"/>
<association property="c" select="ljy.mapper.ClassMapper.selectByIdStepTwo" column="cid"/>
</resultMap>
<select id="selectByidStepOne" resultMap="selectByIdMoreStep">
select sid,name,cid from t_stu where sid = #{sid}
</select>
注意:<association property="c" select="ljy.mapper.ClassMapper.selectByIdStepTwo" column="cid"/>
property是Student里的CLASS里的c
select 是第二次搜索的select方法的id
column是传的参数
第二次搜索
<select id="selectByIdStepTwo" resultType="CLASS">
select cid,classN from t_class where cid =#{cid};
</select>
延迟加载
延迟加载(lazy load) (也称为懒加载,也叫延迟实例化,延迟初始化等)主要表达的思想就是:把对象的创建延迟到使用的时候创建,而不是对象实例化的时候创建。延迟加载机制是为了避免一些无谓的性能开销而提出来的,这种方式避免了性能的浪费。所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。
如果我们不想要某个延迟加载的话------》
一对多映射:
第一种方法
以t_class为主表
在class类里面要给出关联,多加一个list集合来装t_stu表的数据
以及stus的get和set方法
核心代码:
<resultMap id="Classtwo" type="CLASS">
<id property="cid" column="cid"/>
<result property="classN" column="classN"/>
<collection property="stus" ofType="Student">
<id property="sid" column="sid"/>
<result property="sName" column="name"/>
</collection>
</resultMap>
<select id="selectByCid" resultMap="Classtwo">
select c.cid,c.classN,s.sid,s.name from t_class c left join t_stu s on c.cid=s.cid where c.cid = #{cid};
</select>
要把student里面之前写的关联t_class表的东西删了;
第二种方法:
和之前的多对一一样,分步查询;
MyBatis的缓存
缓存: cache
缓存的作用:通过减少IO的方式,来提高程序的执行效率。
mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取,不再查数据库。一方面是减少了IO,另一方面不再执行繁琐的查找算法。效率大大提升。
一级缓存
不用自己开启,mybatis自动开启
同一个sqlsession是有缓存的
那一级缓存什么时候失效呢?
二级缓存
什么时候二级缓存失效呢?
Mybatis逆向工程
让我们体验下怎么自动生成相关文件
第一步:环境准备
打包方式jar
第二步:在pom中添加逆向工程的插件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.powernode</groupId>
<artifactId>mybatis-012-generator</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 逆向工程所引用的插件(需要引入的插件)-->
<!--定制构建过程-->
<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>
</project>
在类的根目录下创建generatorConfig.xml,一定只能是这个名字
内容为:
<?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/mybatis_powernode"
userId="root"
password="123456789">
</jdbcConnection>
<!-- 生成pojo包名和位置 -->
<javaModelGenerator targetPackage="com.powernode.mybatis.pojo" targetProject="src/main/java">
<!--是否开启子包-->
<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>
最后一步:
点击后即可生成;
接下来进行测试:
我们要和之前一样,引入必须的依赖;
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.31</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
工具类也自己引入:
接下来:
把核心配置文件复制过来:
接下来进行测试:
@Test
public void testAuto(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAll();
cars.forEach(car -> System.out.println(car));
sqlSession.close();
}
增强版:
自动生成更加全面的数据库操作文件
我们重新做下准备工作,按之前的方式,就改下targetRuntime的值。
接下来,来看看怎么使用:
@Test
public void test(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
//查全部
List<Car> cars = mapper.selectByExample(null);
cars.forEach(car -> System.out.println(car));
//按条件查
CarExample carExample = new CarExample();
carExample.createCriteria().andBrandEqualTo("比亚迪");
List<Car> cars1 = mapper.selectByExample(carExample);
cars1.forEach(car -> System.out.println(car));
sqlSession.close();
}
分页管理
普通写法:
核心代码:
<select id="paging" resultType="Car">
select * from t_car limit #{startIndex},#{pageSize}
</select>
List<Car> paging(@Param("startIndex") int startIndex,@Param("pageSize")int pageSize);
@Test
public void pagingTest(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
int page = 2;
int pageSize = 3;
int stratIndex=(page-1)*pageSize;
List<Car> paging = mapper.paging(stratIndex, pageSize);
paging.forEach(p-> System.out.println(p));
sqlSession.close();
}
pageHelper 分页插件
1、POM依赖
Mybatis的配置就不多提了。PageHelper的依赖如下。
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.0</version>
</dependency>
2、Mybatis对PageHelper的配置
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
3.使用
<select id="autoPaging" resultType="Car">
select * from t_car
</select>
List<Car> autoPaging();
@Test
public void autoPaging(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
PageHelper.startPage(2,3);
List<Car> cars = mapper.autoPaging();
cars.forEach(car -> System.out.println(car));
sqlSession.close();
}