ORM
O (Object) jvm中的对象
R (Relational) 关系型数据库
M (Mapping) 映射
- 支持定制化 SQL、存储过程、基本映射以及高级映射
- 避免了几乎所有的JDBC代码中手动设置参数以及获取结果集
- 支持XML开发,也支持注解式开发。[为了保证sql语句的灵活,所以mybatis大部分是采用XML方式开发。]
- 将接口和Java 的 POJOs(Plain rdinary Java Object,简单普通的Java对象)映射成数据库中的记录体积小好学:
- 两个jar包,两个XML配置文件。
- 完全做到sal解耦合
- 提供了基本映射标签
- 提供了高级映射标签
- 提供了XML标签,支持动态SQL的编写
开发第一个 Mybatis 程序
resources目录:
放在这个目录当中的,一般都是资源文件,配置文件,直接放到resources目录下的资源,等同于放到了类的根路径下。开发步骤
第一步:打包方式jar
第二步:引入依赖
- mybatis依赖
- mysgl驱动依赖
第三步:编写mybatis核心配置文件: mybatis-config.xml大
注意:
- 第一: 这个文件名不是必须叫做mybatis-config.xml,可以用其他的名字。只是大家都采用这个名字.
- 第二:这个文件存放的位置也不是固定的,可以随意,但一般情况下,会放到类的根路径下。
从XML 中构建 SglSessionFactory通过官方的这句话,
你能想到什么呢?
第一:在MyBatis中一定是有一个很重要的对象,这个对象是: SqlSessionFactory对象。
第二: SqlSessionFactory对象的创建需要XML。XML是什么?
它一定是一个配置文件mybatis中有两个主要的配置文件:
其中一个是: mybatis-config.xml,这是核心配置文件,主要配置连接数据库的信息等。(一个)
另一个是: XxxxMapper.xml,这个文件是专门用来编写SQL语句的配置文件。(每个表一个)
在resources 目录下新建,前者引用后者
mybatis-config.xml
1 sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "指定的环境");事务管理:transactionManager.type = “JDBC” / “MANAGED”
1 |
|
数据源(dataSource)
为程序提供 Connection 对象。此规范是jdk规定的(要实现 javax.sql.DataSource)
dataSource.type=”POOLED” 实际是在指定数据库的连接池。 此外还有(druid,c3p0,dbcp…)
mybatis 自带的数据源:
- UNPOOLED:不使用数据库连接池技术,每次请求都是创建新的Connection对象
- POOLED:使用mybatis自己实现的数据库连接池
- JNDI:集成其他第三方数据库连接池
JNDI 是一套规范,谁实现了它?
大部分web容器都实现了它,如 tomcat, webLogic, Jetty, WebSphare
1 | <dataSource type="JNDI"> |
连接池的配置
poolMaximumActiveConnections
– 在任意时间可存在的活动(正在使用)连接数量,默认值:10poolMaximumIdleConnections
– 任意时间可能存在的空闲连接数。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 时适用)。
1 | // 配置最大连接数 |
properties 属性配置
1 | // 设置 |
使用 properties 文件方式配置
在resource 目录下新建 jdbc.properties
1 | driver=com.mysql.cj.jdbc.Driver |
映射到 mybatis-config.xml 中 property 中
1 | <properties resource="jdbc.properties" /> |
使用
1 | <environment id="development"> |
CatMapper.xml
namespace 指定命名空间,那么多个mapper 文件出现相同的id也不会冲突了
1 |
|
程序 CatMapper.xml 中的 insert 语句
1 | SqlSession sqlSession = null; |
细节:
mybatis 中sql语句可以省略 ”;“
Resource.getResourceAsStream() 是从 类路径下查找,大部分resource都是这样的
Resource.getResourceAsStream 底层实际上调用的是:
1 InputStream is = Classloader.getSystemClassLoader().getResourceAsStream("xxx.xml");核心配置文件名字不一定非的是 mybatis-config.xml
1
2
3
4 // 类路径
<mapper resource="CatMapper.xml"/>
// 绝对路径
<mapper utl="file:///d:/CatMapper.xml"/>
mybatis 事务管理机制
配置:
1
2
3
4 // mybatis自己采用原生JDBC方式管理事务
<transactionManager type="JDBC"/>
// mybatis 不在负责事务,交给其他容器管理。如:spring
<transactionManager type="MANAGED"/>
封装 SqlSession 对象
1 | package utils; |
Mybatis 日志
settings 标签要出现在 environments 标签之前,STDOUT_LOGGING 为 mybatis 自己实现的日志格式,其他格式需要引入第三方库 如 SLF4J
1 | <settings> |
集成logback 日志框架
pom 中引入依赖
1
2
3
4
5
6<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
<scope>test</scope>
</dependency>配置文件 logback.xml/logback-test.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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>
<!-- mybatis log configure -->
<logger name="com.apache.ibatis" level="TRACE" />
<logger name="java.sql.Statement" level="DEBUG" />
<logger name="java.sql.PreparedStatement" level="DEBUG" />
<logger name="java.sql.Connection" level="DEBUG" />
<!-- 日志输出级别, TRACE < DEBUG < INFO < WARN < ERROR -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
使用 mybatis 完成CRUD
C:Create 增
使用 #{} 代价 ?占位符。 执行 sqlSessqion.insert() 时可传入一个Map集合,此时 #{map集合的key}
1
2
3
4<insert id="insertCar">
insert into t_car(id, car_num,brand,guide_price, produce_time, car_type)
values(null, #{}, #{},#{},#{},#{})
</insert>建立 pojo类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81public class Car {
// 建议使用包装类,防止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 String toString() {
return "Car{" +
"id=" + id +
", carNum='" + carNum + '\'' +
", brand='" + brand + '\'' +
", guidePrice=" + guidePrice +
", produceTime='" + produceTime + '\'' +
", carType='" + carType + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCarNum() {
return carNum;
}
public 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;
}
}pojo 类实例化
1
2
3
4Car car = new Car(null, "3333", "保时捷",40.0,"2020-10-12","新能源");
/*
执行 sqlSessqion.insert() 时传入 car ,#{}中写pojo类的属性名
*/R:Retrieve 查
单个查询
1
2
3
4
5
6
7
8<select id="selectCar" resultType="com.pojo.Car">
select * from t_car where id=#{id}
</select>
// sqlSession.selectOne("selectCar", 37)
<!--
老版本 mybatis 好像会有 字段对应问题 如 pojo 类中的小驼峰命名与数据库的下划线命名会导致查询不到字段的值, 查询时需要用 as 语法赋值别名与 pojo类中的一致
-->列表查询
1
2
3
4
5<select id="selectCarAll" resultType="com.pojo.Car">
select * from t_car
</select>
List<Car> rs = sqlSession.selectList("selectCarAll");U:Update 改
1
2
3
4
5<update id="updateCar">
update t_car set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType} where id = #{id}
</update>
// sqlSession.update("updateCar", car)D:Delete 删
1
2
3
4
5
6<delete id="deleteCar">
delete from t_car where id = #{id}
</delete>
// 如果#{} 只有一个,那么中间的属性可以随便写,最好见名知意
sqlSession.delete("deleteCar", 37)
dom4j 解析xml文件
依赖
1 | <dependency> |
配置文件
1 |
|
解析xml配置文件
1 | public class ReadXMLTest { |
javassist
可以在内存中生成类,接口,添加方法。。。
Dao 层的接口实现类 代码非常固定,简单。可以使用 javassist 生成实现类
1 | // 获取类池,用来生成class |
为 接口 Delete 添加 实现类 DeleteImpl
1 | // 获取类池 |
mybatis 底层使用这个方式帮我们自动生成 DAO 接口的代理类,然后实现这个类的实现类
1 |
|
ThreadLocal
每一个线程都有属于自己的 SqlSession ,改造 SqlSessionUtils
1 | public class SqlSessionUtil { |
mybatis 小技巧
#{},${} 区别
#{}:底层使用 PreparedStatement。先编译再给SQL语句?传值。避免SQL注入
${}:底层使用 Statement。先精选SQL语句拼接,然后再对SQL 语句进行白泥一。存在SQL注入风险
查询排序时会用到 ${}
分表查询拼接表名查询,如:t_log20201012,前端传日期,后端拼接
批量删除
or
1
delete from t_car where id=1 or id=2 or id=3;
int
1
2
3delete from t_car where id in (1,2,3);
int deleteBatch(String ids);// 多id,分割
模糊查询
1 | select * from t_car where brand like '%比亚迪%' |
别名配置
1 | <!-- |
mapper配置
1 | // 类的根路径下查找资源 |
配置模板文件
去 settings -》 FIle and code Template 新增模板
alt+insert 新建快捷键
使用自动生成的主键
自动生成一条自增数据,id自增我们就不知道生成的是多少。如果我们要用到id,难道还要再查一次么?
配置生成主键
1 | // useGeneratedKeys: 开启使用自动生成主键 |
获取主键值
1 | SqlSession sqlSession = SqlSessionUtil.openSession(); |
mybatis 参数处理
简单类型参数
7中基本类型+包装类+String+两种时间类型
byte,short,int,long,float,double,char
Byte,Short,Integer,Long,Float,Double,Character
String
java,util.Date, java.sql.Date
当接口中方法参数只有一个,并且都是简单类型时。此时mybatis 可以自动推断,不用指定参数类型
1 | public interface Student { |
1 | <!-- |
参数是 Map 类型
mybatis 也能自动类型推断
1 | list<Map> insertStudentByMap(Map map); |
1 | <insert id="insertStudentByMap" parameterType="map" > |
参数是实体
参数是一个 pojo 实体类,此时mybatis 也可以自动类型推断
参数之多参数
mybatis 会创建一个数组把多个参数放进去
map.put(“arg0”, name);
map.put(“arg1”, sex);
map.put(“arg2”, xxx);
1 | List<Student> selectByNameAndSex(String name, Chaecter sex); |
1 | <select id="selectByNameAndSex" resultTpet="Student"> |
参数之 @params 注解
底层实现
map.put(“name”, “xxx”);
map.put(“sex”, “男”)
1 | List<Student> selectByNameAndSex(; String name, Chaecter sex) |
1 | <select id="selectByNameAndSex" resultTpet="Student"> |
Mybatis 查询专题
返回Car
查询返回pojo实体 Car。 模糊查询返回必须采用List 集合接收,因为可能返回多个结果。 如果单个查询用List 集合接收,这样是可以的
1 | Car selectById(String id); |
1 | // 数据库的下划线命名与pojo类的小驼峰命名冲突可能返回null 值 |
返回Map
返回map, key 是表字段名,值是对应属性值
1 | // 返回一个map |
1 |
|
结果映射
当数据库的下划线命名与pojo类的小驼峰命名冲突可能返回null 值的情况,
普通就是在查询是添加别名,当时字段多时比较麻烦
1 | List<Car> selectAllByResultMap(); |
1 | <!-- |
开启驼峰命名自动映射
java命名:小驼峰 eg: myMethodPost
mysql命名:将小驼峰改为 下划线:my_method_post
mybatis-config.xml
1 | <settings> |
返回总记录条数
查询所有,但是要返回总记录条数
1 | <select id="selectTotal" resultType="long"> |
动态SQL
需要拼接sql 如批量删除
1
2 // id 数组是动态的
delete from t_car where id in (1,2,3...)
if标签
1 | List<Car> selectByMultiCondition ( |
1 | <select id="selectByMultiCondition" resultType="Car"> |
where 标签
where标签的作用: 让where子句更加动态智能所有条件都为空时,where标签保证不会生成where子句.自动去除某些条件前面多余的and或or。
1 | <select id="selectByMultiCondition" resultType="Car"> |
trim标签
trim标签的属性
- prefix:在trim标签中的语句前添加前缀
- suffix: 在trim标签中的语句后添加后缀
- prefixOverrides: 去掉前缀
- suffixOverrides: 去掉后缀
1 | <select id="selectByMultiCondition" resultType="Car"> |
set 标签
主要使用在update语句当中,用来生成set关键字,同时去掉最后多余的 “,” 比如我们只更新提交的不为空的字段,如果提交的数据是空或者 null,那么这个字段我们将不更新。
1 | <updateid="updateWithSet" |
choose when otherwise
三个标签通常一起使用,相当于if else, 只要有一个条件成立,就结束
1 | <select id="" > |
批量删除
使用 foreach 标签
1 | int deleteByIds(; String[] ids) |
1 | <delete id="deleteByIds" > |
批量插入
1 | int insertBatch(; List<Car> cars) |
1 | <insert id="insertBatch"> |
sql标签,include标签
sql标签用来声明sql片段,include标签用来将声明的sql片段包含到某个sql语句中
提高代码复用性
1 | <sql id="yy" > |
数据库高级映射
多对一映射
t_stu
sid | sname | cid |
---|---|---|
1 | 张三 | 1000 |
2 | 李四 | 1000 |
3 | 王五 | 1001 |
1 | public class Student { |
t_class
cid | cname |
---|---|
1000 | 高三一班 |
1001 | 高三二班 |
1 | public class Clazz { |
1 | <!-- |
启用全局懒加载
信息
1 | <settings> |
一对多
一对多,一在前,一是主表,多是副表
1 | public class Clazz { |
1 | <!-- |
mybatis 缓存
执行SQL 查询的时候,将查询结果放到缓存中。如果下次还是执行完全相同的sql语句。直接从缓存拿,不从硬盘找数据
mybatis 缓存包括
一级缓存:将查询数据存到SqlSession 对象 , 针对当前会话,默认开启
只要两次调用相同sql查询。
啥时候以及缓存失败?
第一次DQL和第二次DQL之间做了下面任意一件事,都会清空一级缓存
- 执行 sqlSession,cleanChache()
- 执行 insert,delete,update 任意一个语句
二级缓存: 将查询数据存到SqlSessionFactory 针对所有会话
1
2
3
4
5//1、 默认就是 true 无需全局配置
<setting name="chachceEnable" value="true">
//2、 使用二级缓存的mapper.xml文件需局部配置 <cache />
//3、 使用二级缓存的实体类必须是可序列化,既必须实现 java.io.Serializable
//4、 Sqlsession 对象关闭或提交后,一级缓存数据在会被写到二级缓存第三方缓存,三级缓存:三级缓存是用来代替mybatis 自带的二级缓存
常见:EhCache,Memcache
myBatis 逆向工程
所谓的逆向工程是: 根据数据库表逆向生成Java的pojo类,SalMapperxml文件,以及Mapper接口类等.
要完成这个工作,需要借助别人写好的逆向工程插件。
思考:使用这个插件的话,需要给这个插件配置哪些信息?
- ·pojo类名、包名以及生成位置.
- SglMapper.xml文件名以及生成位置
·- Mapper接口名以及生成位置。
- 连接数据库的信息。
- 指定哪些表参与逆向工程
新建一个maven项目
修改 pom.xml
1 | <packaging>jar</packaging> |
新增 generatorConfig.xml,放在类的根路径
1 |
|
从maven找到插件 mybatis-generator,展开。 双击mybatis-generator:generate
MyBatis逆向工程dao接口只生成两个方法,原因:
1.是你的mysql版本太高了,换成6一下的版本即可
2.看看你的表有没有设置主键,如果没有设置一下即可
3.如果想使用高版本的mysql在url后面加nullCatalogMeansCurrent=true
PAgeHelper 分页插件
limit 分页
1 | /* |
使用 pageHelper
1
2
3
4
5 <dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
mybatis-conf.xml
1 | <plugins> |
注解是开发
可以不用写 mapper.xml 文件了。但只是针对简单的语句
1 |
|
- 本文作者: 王不留行
- 本文链接: https://wyf195075595.github.io/2023/02/14/programming/java/myBatis/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!