自己做网站去哪买服务器,那类型网站容易做排名,下载牛霸软件,cms建站系统免费目录
概述
mybatis做了什么
原生JDBC存在什么问题
MyBatis组成部分
Mybatis工作原理
mybatis和hibernate区别
使用mybatis#xff08;springboot#xff09;
mybatis核心-sql映射文件
基础标签说明
1.namespace#xff0c;命名空间
2.select#xff0c;insertspringboot
mybatis核心-sql映射文件
基础标签说明
1.namespace命名空间
2.selectinsertupdatedelete为不同类型的sql标签
映射标签的属性说明
1.id
2.parameterType
简单类型
实体类
实体类嵌套
Map
Param注解
3.resultType
简单类型
实体类
实体类列表
Map
4.resultMap标签
resultMap属性
id标记这个resultMap通过id可以使用该resultMap
type指定映射的实体类
result
association
collection
5.jdbcType的作用
常见类型关系
6.useGeneratedKeys和keyProperty组合
7.statementType
8.useCacheflushCache
mybatis缓存
属性说明
开启缓存后还可以在任意的具体sql中配置缓存的访问控制
select语句
insert,update,delete语句
动态sql标签
1.if test...
注意点
判空
判断某个值
嵌套
2.where
3.set
4.choosewhenotherwise
5.isNotEmpty propertyxxx...
6.isEqual propertyxxx compareValue1
sql标签
foreach标签
属性说明
注意
如果入参直接传递一个List或者Array
bind标签
selectKey标签
作用
注意
属性
keyProperty
keyColumn
resultType
order
statementType
CDATA
${}和#{}区别
#{value}
${value}
mybatis插件
mybatisX自动生成基础代码 概述
前身是apache开发的iBatis迁移到goole code后改名为MyBatis2013年迁移到GitHub
在springboot的背景下我们不关注原本ssm项目的xml配置只关注mybatis本身
mybatis做了什么
解决了原生jdbc的弊端开发者只需要关注sql本身
原生JDBC存在什么问题
1.频繁的获取连接和释放资源
每次需要执行JDBC就会获取连接执行结束释放资源浪费系统资源
解决mybatis使用数据库连接池解决问题
2.将sql语句硬编码变动sql就要改变java代码
解决将Sql语句配置在XXXXmapper.xml文件中与java代码分离
3.sql传参麻烦参数数量可能会变化而占位符和参数数量要保持一致
解决mybatis自动映射
4.处理结果也是硬编码sql变化导致解析结果变化就要改变java代码
解决mybatis自动映射
MyBatis组成部分
mapper.xmlsql映射文件配置了操作数据库的sql语句此类文件需要在SqlMapConfig.xml中加载
SqlSessionFactoryBuilderSqlSessionFactoryBuilder用于创建SqlSessionFacotySqlSessionFacoty是单例所以一旦创建完成就不再需要SqlSessionFactoryBuilder
SqlSessionFactory即会话工厂MyBatis配置完整就可以获取SqlSessionFactory
sqlSession即会话由会话工厂SqlSessionFactory创建操作数据库增删改查需要在会话中进行通常来说每个线程创建各自的sqlSession使用完就close()
Executor操作数据库mybatis底层自定义了Executor执行器接口操作数据库Executor接口有两个实现一个是基本执行器一个是缓存执行器
Mapped StatementMapped Statement也是mybatis一个底层封装对象可以简单的理解为一条完整的sql入参查询返回就是一个Mapped Statement对象 1.它封装了mybatis配置信息及sql映射信息等mapper.xml文件中一个sql对应一个Mapped Statement对象sql的id即是Mapped statement的id 2.它对sql执行输入参数进行定义包括HashMap、基本类型、pojoExecutor通过Mapped Statement在执行sql前将输入的java对象映射至sql中输入参数映射就是jdbc编程中对preparedStatement设置参数 3.它对sql执行输出结果进行定义包括HashMap、基本类型、pojoExecutor通过Mapped Statement在执行sql后将输出结果映射至java对象中输出结果映射过程相当于jdbc编程中对结果的解析处理过程
Mybatis工作原理
JDK动态代理Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象MappedProxy代理对象会拦截接口方法根据类的全限定名方法名唯一定位到一个MapperStatement并调用执行器执行所代表的sql然后将sql执行结果返回
mybatis和hibernate区别
1.mybatis是半自动ORM框架需要程序员编写sql更灵活因此更容易满足复杂场景能更好的控制sql性能
2.mybatis由于要开发者自己编写sql意味着不同数据库就要学习不同的语法编写不同的sql可能增大学习成本和工作量
3.mybatis门槛低几乎可以说只要会写sql就等于会用mybatis而hibernate的配置更繁琐个人学习成本很高同时要求团队的所有人都需要精通hibernate不利于团队开发效率
4.hibernate不需要开发者关注sql而是集中精力在业务逻辑上但在处理复杂场景时更繁琐甚至可以说繁琐的有些变态一个动态条件在mybatis中是一个标签两行代码而hibernate可能需要写几十倍的代码来实现
5.随着mybatis-plus的出现绝大部分的WEB项目更倾向于选用mybatis
使用mybatisspringboot
1.在springboot的背景下不再需要自己配置xml通过直接导入启动器依赖即可
2.配置Mapped Statement的文件路径让mybatis知道要执行的sql位置
3.编写mapper接口
4.编写mapper接口对应的Mapped Statement
dependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion2.2.2/version
/dependencymybatis:mapper-locations: classpath:mapper/*.xmlconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplMapper
public interface CustomerMapper {int insert(Customer record);
}?xml version1.0 encodingUTF-8?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.web.mapper.CustomerMapperinsert idinsert keyColumnid keyPropertyid parameterTypecom.entity.CustomeruseGeneratedKeystrue/insert
/mapper
mybatis核心-sql映射文件
?xml version1.0 encodingUTF-8?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.web.mapper.CustomerMapperinsert idinsert keyColumnid keyPropertyid parameterTypecom.entity.CustomeruseGeneratedKeystrue/insert
/mapper
基础标签说明
1.namespace命名空间
指向mapper接口的位置mapper接口的方法名对应这里sql的id
每个命名空间都是独立的不同命名空间可以存在相同id的sql
比如TeacherMapper和StudentMapper都可以配置id为queryById的sql
2.selectinsertupdatedelete为不同类型的sql标签
映射标签的属性说明
1.id
statement的id可以理解为sql的标识
2.parameterType
输入参数类型在sql中使用#{value}${value}获取到入参二者是不同的后面单独说明
简单类型
比如IntegerString
#{value}${value}的value是接口方法的形参名
select * from student where id #{value};
实体类
#{value}${value}的value是实体类的属性名
实体类需要有gettersetter方法否则mybatis无法操作实体类的成员取值赋值
select * from student where name #{name} and age #{age};
实体类嵌套
实体类的一个属性是另外一个实体类
包装对象要implements serializable实现序列化接口
使用#{student.name}${student.name}获取属性
Map
#{value}${value}的value为map的key
select * from student where name #{name} and age #{age};
Param注解
低版本的mybatis只传一个参数时必须在接口上使用Param声明该参数
高版本的mybatis只传一个参数时不会报错传多个参数不使用实体类封装时每个参数都要用Param声明
3.resultType
结果映射类型 sql查询结果的类型
简单类型
sql结果为简单类型比如intString
select count(*) from student;
实体类
sql结果为一条记录包含多个字段封装到实体类对象中比如Student
select * from student where id #{value};
实体类列表
sql结果为多条记录包含多个字段分别封装到实体类中即对象集合/数组
虽然结果是List但实际配置的resultType仍然为实体类即List中所存对象的类型
select * from student;
Map
直接用map接收会生成List这样的结构
查询结果列名即为map的keymap.get(xxx)可以获取结果的值
可以自定义指定哪一列映射到key哪一列映射到value但这本质上并不是Map只是看起来像
4.resultMap标签
我们刚刚知道sql结果可以映射到实体类上但这样就必须要求sql中的字段名和实体类的属性名相同才行
实际上往往数据库的字段名和实体类的属性名是不同的比如数据库是customer_id而实体类是customerId当然我们可以在sql上给字段写别名就可以让结果映射到实体类上但这样意味着每条sql都要写一遍这些别名显然不合理
要想自动让查询结果映射到实体类可以使用resultMap标签resultMap标签可以将查询结果映射到某个给定的实体类上
resultMap属性
id标记这个resultMap通过id可以使用该resultMap
type指定映射的实体类
result
property实体类字段名column表字段名jdbcType数据类型
association
主实体类的字段是实体类一对一关系
property主实体类的字段名javaType子实体类类型column关联表的主键
collection
主实体类的字段是实体类一对多关系
property主实体类的字段名ofType子实体类类型javaType集合类型
resultMap idStuResultMap typecom.coolway.bean.Student
!--主键--id propertyid columnID jdbcTypeVARCHAR/result propertyname columnNAME jdbcTypeVARCHAR/association propertyteacher javaTypeTeachercolumnteacher_idid propertyid columnteacher_id/result propertyteacherName columnteacher_name//associationcollection propertysubjects ofTypeSubject javaTypelistid propertyid columnpost_id/result propertysubject columnpost_subject//collection
/resultMap
//一个学生对应一个老师一个学生对应多门课程
select idxxxqueryForResultMap resultMapStuResultMapselect s.id, s.name stu_name, s.age stu_age, t.id teacher_id, t.name teacher_name, t.age teacher_age,sj.id subject_id, sj.name subject_namefrom student sleft join teacher t on t.id s.teacher_idleft join subject sj on sj.id s.subject_id
/select
5.jdbcType的作用
jdbcType会用在#{}中作用是标记数据库字段的类型
有了给定类型mybatis就知道拿到入参后应当作为什么类型传递给数据库拿到数据库结果以后需要处理成什么类型
在入参为空或者结果为空时也知道应当给定什么默认值在这种情况下显然我们不能使用基本类型而是应当使用包装类传递数据否则值在一些情况下不会是null而是默认值
常见类型关系
java类型 jdbcType
String VARCHAR
LocalDate DATE
LocalTime TIME
LocalDateTime TIMESTAMP
Byte/Short/Integer/Long/Float/Double/BigDecimal NUMERIC其中基本类型也有自己对应的类型
6.useGeneratedKeys和keyProperty组合
在insert语句中组合使用可以获取指定字段操作后的值放到入参实体中显然通常用来获取自增主键自生成字段
useGeneratedKeystrue keyPropertyid
7.statementType
通常不需要设置默认即可
用来标记何种方式操作sql
STATEMENT 直接操作sql不进行预编译获取数据
PREPARED 预处理参数进行预编译获取数据默认
CALLABLE 执行存储过程
8.useCacheflushCache
通常不需要设置默认即可
mybatis缓存
mybatis查询会先查询缓存数据当存在入参一致的数据时直接将缓存返回不再查询数据库
一级缓存是针对sqlSession每次操作数据库都会生成sqlSession对象缓存数据就存在sqlSession中显然不同的sqlSession不会互相影响
二级缓存是针对mapper多个sqlSession操作同一个mapper的sql语句可以共用这个mapper的二级缓存显然二级缓存是跨sqlSession的
sqlSession理解为JDBC中的Connection即针对数据库的一次连接
二级缓存需要手动开启
application.yml mybatis: configuration: #开启MyBatis的二级缓存 cache-enabled: true mapper.xml cache evictionFIFO flushInterval600000 size4096 readOnlytrue/
属性说明
eviction缓存回收策略
LRU最少使用原则移除最长时间不使用的对象
FIFO先进先出原则按照对象进入缓存顺序进行回收
SOFT软引用移除基于垃圾回收器状态和软引用规则的对象
WEAK弱引用更积极的移除移除基于垃圾回收器状态和弱引用规则的对象
flushInterval刷新时间间隔单位为毫秒这里配置的100毫秒。如果不配置那么只有在进行数据库修改操作才会被动刷新缓存区
size引用额数目代表缓存最多可以存储的对象个数
readOnly是否只读如果为true则所有相同的sql语句返回的是同一个对象有助于提高性能但并发操作同一条数据时可能不安全如果设置为false则相同的sql后面访问的是cache的clone副本
开启缓存后还可以在任意的具体sql中配置缓存的访问控制
useCache标记是否启用mybatis缓存启用缓存后当入参一样mybatis将会直接将缓存中的结果返回而不再查询数据库每次查询后会缓存到缓存中
flushCache标记是否在操作后清空缓存为了防止读到脏数据增删改之后应当清空缓存
select语句
useCache默认为true即每次查询后会将结果缓存到缓存中
flushCache默认为false即每次操作后不会清空缓存
insert,update,delete语句
没有useCache属性
flushCache默认为true即每次操作后清空缓存
动态sql标签
1.if test...
if test...
满足if则拼接该段sql常用于动态条件查询
select idqueryStudentByMap parameterTypeMap resultTypecom.coolway.bean.Studentselect * from student twhere 11if testname ! null and name ! AND t.name like %${name}%/ifif testage ! null and age! AND t.age #{age}/if
/select这里有一个where 11这是因为如果第一个if成立sql就为where and ....语法错误如果不想用where 11还要规避这样的问题那么可以使用where标签
注意点
判空
对于非String类型的数据在判空时只需要判断 !null如果 !会报错
判断某个值
对于String类型判空需要xxx ! null and ! 如果要判断是否等于某个值
嵌套
if标签可以嵌套
如下是错误的
if teststatus! null and statusOK...
/if
因为MyBatis是使用的OGNL表达式来进行解析的要改成
if teststatus! null and status OK ...
/if
或者
if teststatus! null and statusOK.toString()...
/ifif标签嵌套
if testdata ! nullif testdata.types !null and data.types ! AND T.TYPES #{data.types}/ifif testdata.status !null and data.status ! AND T.STATUS #{data.status}/if
/if
2.where
where
自动添加where关键字同时去掉sql语句中第一个无关的and关键字
但要注意and前面不能有注释否则就没法自动去掉了这可能是低版本的BUG
3.set
set
自动添加set关键字同时去除末尾的无关逗号
UPDATE T_ABOLISH T
setif testabolishReason ! null and abolishReason ! T.ABOLISH_REASON #{abolishReason},/ifif testabolishUserName ! null and abolishUserName ! T.ABOLISH_USER_NAME #{abolishUserName},/if
/set
4.choosewhenotherwise
choose when otherwise相当于if else
choosewhen test//.../whenotherwise//.../otherwise
/choose
5.isNotEmpty propertyxxx...
判空
isEmpty propertyxxx.../isEmpty
6.isEqual propertyxxx compareValue1
判断值
isNotEqual propertyxxx compareValue0.../isNotEqual
sql标签
表示sql片段
然后在编写sql时可以通过引用sql片段
select include refidstudentFields/ from student
如果引用别的namespace的sql片段需要在refid的时候加上namespace但通常不会这么做
sql idstudentFieldsid,name,age,sex
/sql
foreach标签
当传递pojo或者map的属性值为List或者Array时需要遍历属性值用foreach
select...select * from studentwhereforeach collectionids itemitem openid in( close) separator,#{item}/foreach/where
/select
属性说明
collection遍历的集合这里是传入pojo/map的属性名
item遍历时的临时变量自定义但是和后面的#{}里面要一致
open在前面添加的 sql 片段上面配置的是 id in(
close在结尾处添加的 sql 片段上面配置的是 )
separator指定遍历的元素之间使用的分隔符上面配置的是,逗号上面的sql就为 select * from student where id in(xx,xx,xx...)
注意
如果入参直接传递一个List或者Array
此时collection属性就不能配置为属性名/key了要为list/array
原因mybatis处理入参的方式是创建一个map把入参放入map中再传给sqlSession执行
对于pojo或者mapper类型属性名为key属性值为value
对于Array属性名为array属性值为数组的值
对于List属性名为list属性值为列表的值
如果必须要用自定义的名称需要在mapper中给变量增加Param(xxx)注解
bind标签
使用 OGNL 表达式创建一个变量井将其绑定到上下文中
兼容不同数据库之间的SQL语法差异对数据库迁移友好
常用于模糊查询能有效防止sql注入
bind name 需要绑定的变量 value 绑定的最终值 /if testcustomerName ! null and customerName ! bind namecustomerName value%customerName%/c.cname like #{customerName,jdbcTypeVARCHAR}
/if
selectKey标签
作用
在insertupdate语句中会通过sql处理某些字段的值通过selectKey可以直接获取某些字段sql运行后的值而不需要单独查询一次处理后的数据提高安全性减少代码冗余
比如针对某个字段要在insertupdate操作的前后改变这个字段的值而且操作后需要知道这个字段变成了什么比如自增主键计数字段
有很多博客说selectKey是用来获取自增主键/主键的实际上并不是selectKey可以获取任意的字段/表达式的值但有一定的限制注意点
注意
1.只能存在于insert或update的子标签中
2.入参只能是pojo不能是String等类型
3.返回值并非是selectKey的字段仍然是update原本的返回值即被update的记录数
4.我们所需要的selectKey字段实际上是更新到被传入的pojo实例中
属性
keyProperty
结果集映射目标类的属性
若存在多个,则使用逗号分隔
keyColumn
目标类的属性,映射结果集的列名
若存在多个,则使用逗号分割
resultType
设置返回类型
可使用别名
order
设置此selectKey的执行顺序是早于sql语句,还是晚于sql语句
候选值BEFORE和AFTER;
statementType
设置sql语句的映射类型默认即可
候选值STATEMENT,PREPARED,CALLABLE
serviceImpl
NumberDo numberDo numberMapper.getNumberById(id);
if (numberDo ! null) {numberMapper.plusNumberById(numberDo);//注意这里怎么获取更新后的新值return numberDo.getNumber();
}mapper.xml
update idplusNumberById parameterTypecom.coolway.testProject.dal.number.NumberDoselectKey resultTypejava.lang.Integer keyColumnINS_NUMBER keyPropertyinsNumber orderAFTERSELECT T.INS_NUMBER FROM T_NUMBER T WHERE T.ID #{id}/selectKeyUPDATE T_NUMBER TsetT.INS_NUMBER T.INS_NUMBER 1/setWHERE T.ID #{id}
/update
CDATA
![CDATA[......]]
当sql包含符号时会报错。这是因为会被xml解析为新元素的开始会被xml解析为字符实体的开始
解决方式有两种虽然并不会报错但仍然建议把他们做同样处理
1.使用转义替换
lt; 小于
gt; 大于
amp; 和号
apos; 省略号
quot; 引号
select * from student where age lt 18;
2.使用![CDATA[......]]
对于包含大量特殊符号的sql建议使用
解析器会忽略 CDATA 部分中的所有内容
注意
CDATA 部分不能包含字符串 ]]。也不允许嵌套的 CDATA 部分
标记 CDATA 部分结尾的 ]] 不能包含空格或换行
${}和#{}区别
#{value}
sql语句中的占位符会根据参数类型进行预编译可以防止sql注入
对于传入的String类型value占位符#{}会加上单引号即value
select * from student t where t.name like #{name};
select * from student t where t.name like 张三
而在模糊查询或者内存中传入一个sql片段显然#{}不符合要求所以要用到${value}
select * from student t where t.name like %#{name}%;
select * from student t where t.name like %张三%
${value}
用于sql中的字符串拼接不会预编译不能防止sql注入
模糊查询的sql注入问题可以通过bind标签解决也可以通过数据库提供的某些函数解决
select * from student t where t.name like ${value};
select * from student t where t.name like 张三
select * from student t where t.name like %${value}%;
select * from student t where t.name like %张三%
mybatis插件
经过上面的学习我们发现mybatis上手是很简单会写sql会用mybatis但对于每一张表或者每一类业务我们都需要编写resultMap返回值映射包含所有字段的insertdeleteupdateselect基础语句相当于我们要做很多次重复的事情既麻烦又枯燥
有没有什么办法能够解决这个事情呢当然有mybatis提供了逆向工程工具可以通过数据库表结构自动生成resultMap返回值映射包含所有字段的insertdeleteupdateselect基础语句但这个逆向工程需要我们编写一部分代码而一些mybatis插件比如mybatisXmybatisPro等等可以用图形化操作界面只需要连接上数据库配置指定的包路径就能实现这些基础代码的生成十分方便快捷自由度也高
mybatisX自动生成基础代码
1.安装mybatisX插件
2.连接数据库
通过IDEA的database连接数据库
报错Server returns invalid timezone. Go to ‘Advanced’ tab and set ‘serverTimezone’ property manually
解决设置时区为当前时区Advanced-找到serverTimezone设置为Asia/Shanghai
3.生成mybatis代码
右击表名选择mybatisX-generator
选择需要生成的类型如果不使用mybatisplus选择
annotation:none
template:default-all
点击确认会发现项目中指定包下已经生成了实体类mapper接口mapper.xml已经自动生成了resultMap返回值映射包含所有字段的insertdeleteupdateselect基础语句