Java - MyBatis (下)
Mybatis(下)
一、代码优化
1. Mybatis核心代码中的数据库连接所需的这些属性值,可以通过properties文件外部注入
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
相应变动
<!-- 引入外部配置文件 -->
<properties resource="db.properties"> </properties><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>
2. 与Mapper接口对应的XML文件中CRUD标签中 parameterType 和 resultType 的对应的包名是全名太长,可以起个别名在MyBatisXML中,之后就可以直接使用别名。
起别名
<!-- 起别名 --><typeAliases><typeAlias type="org.example.User" alias="User" /></typeAliases>
对应变化
<select id="getUserById" resultType="User" parameterType="int">select * from mybatis.userwhere id=#{id}</select><insert id="addUser" parameterType="User">insert into mybatis.user(id,name,pwd)values (#{id},#{name},#{pwd})</insert>
3. 传参方式:
-
传单个基本类型、传对象、传map
-
我们的实体类或者数据库中的表,字段或者参数过多时,我们要使用Map进行传参
二、配置解析:
1.MyBatis核心配置文件
properties(属性)
settings(设置)
typeHandlers(类型别名)
objectFactory(对象工厂)
plugins(插件)
environments(环境变量)environment(环境变量)transactionManager(事务管理器)dataSource(数据源)
databaseldProvider(数据库厂商标识)
mappers(映射器)
properties(属性):
关于数据库中的属性配置,可以直接引入外部文件properties文件。
typeHandlers(类型别名):
为Java类型设置一个短的名字,它只和XML配置有关,就是为了减少类完全限定名的冗余。
settings(设置):
这是MyBatis中极为重要的调整设置,会改变MyBatis的运行时行为。
mappers(映射器)
使用class文件绑定注册
-
接口和它的Mapper配置文件必须同名
-
接口和它的Mapper配置文件必须在同一个包下
三、生命周期和作用域
生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
-
一旦SqlSessionFactory被创建了,就不再需要SqlSessionFactoryBuilder了
-
最佳作用域:局部变量
SqlSessionFactory:
-
类似于数据库连接池的概念
-
SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个SqlSessionFactory实例。
-
最佳作用域:应用作用域,最简单的就是使用单例模式或者静态单例模式
SqlSession:
-
连接到数据库连接池的一个请求
-
最佳作用域:用完之后赶紧关闭,否则资源被占用
四、解决属性名和字段名不一致的问题
数据库中表的字段名和实体类中的属性名不一致,类型处理器无法映射,找不到。
解决方法:
1.在SQL语句中,给字段名其个别名,为对应的实体类的属性名。
2.其中,使用resultMap结果集映射,替换resultType。
resultMap的设计思想是,对于简单的语句根本不需要配置显示的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。
<mapper namespace="org.example.Mapper"><resultMap id="UserMap" type="User"><result column="pwd" property="password"/></resultMap><select id="getUserList" resultMap="UserMap">select * from mybatis.user</select>
</mapper>
五、日志
日志工厂
如果一个数据库操作出现了异常,我需要排错,日志就是最好的助手。 可以在MyBatis核心配置文件中使用settings配置指定该日志的具体实现。
提供了以下这些工厂的实现类
SLF4J
LOG4J (掌握)
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING (掌握)
NO_LOGGING
STDOUT_LOGGING的实现
<settings>//标准的日志工厂实现<setting name="logImpl" value="STDOUT_LOGGING"/></settings>
LOG4J
简介
什么是LOG4J?
LOG4J是Apache的一个开源项目,通过使用LOG4J,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件。
我们也可以控制每一条日志的输出格式。
通过定义每一条日志信息的级别,我们能够更加细致地控制日志地生成过程。
通过一个配置文件来灵活地进行配置,而不需要修改应用地代码。
LOG4J的实现
1.先导入LOG4J的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
2.在classpath下建立log4j.properties文件
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3.配置log4J为日志的实现
<settings><setting name="logImpl" value="LOG4J"/></settings>
4.LOG4J的使用
如果要在输出日志的类中加入相关语句,就要获取相应的对象
导入包,import org.apache.log4j.Logger;
日志对象:Logger logger = Logger.getLogger(test12.class)
;(当前类的.class)
日志级别:info、debug、error
static Logger logger = Logger.getLogger(test12.class);@Testpublic void log4j(){logger.info("info:进入了log4j");logger.debug("debug:进入了log4j");logger.error("error:log4j");}
六、分页
分页目的:减少数据的处理量
1.使用limit分页
select * from user limit 0,5
2.使用Mybatis实现分页
<select id="getUserList" resultMap="UserMap">select * from mybatis.user limit 6;</select>
3.使用RowBounds分页(用java代码实现分页)
4.分页插件
七、使用注解开发
使用面向接口编程,根本原因是解耦、可拓展、提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好。
不需要使用写对应Mapper接口的XML文件了,但是仅限于简单的sql语句,复杂的就不行了。
在Mybatis核心配置文件中
<!-- 绑定接口 --><mappers><mapper class="org.example.Mapper"/></mappers>
Mapper接口部分
public interface Mapper {//查询全部用户@Select("select * from User")public List<User> getUserList();
}
测试部分不变
@Testpublic void test() {SqlSession sqlSession = Utils.getSqlSession();Mapper mapper = sqlSession.getMapper(Mapper.class);List<User> userList = mapper.getUserList();for (User user : userList) {System.out.println(user);}sqlSession.close();}
八、Mybatis执行流程剖析
Resources获取加载全局配置文件
实例化SqlSessionFactoryBulider构造器
解析配置文件流XMLConfigBuilder
Configuration所有的配置信息
SqlSessionFactory实例化
transactional事务管理器
创建executor执行器
创建SqlSession
实现CRUD
查看是否执行成功(transactional)
提交事务
关闭
九、使用注解CRUD(仅限简单SQL语句)
Mapper接口
public interface Mapper {//查询全部用户@Select("select * from User")public List<User> getUserList();//根据id查询用户@Select("select * from User where id=#{id}")public User getUserById(@Param("id") int idd);//insert一个用户@Insert("insert into User (id,name,pwd) values (#{id},#{name},#{pwd})")public int addUser(User user);//update一个用户@Update("update User set name=#{name},pwd=#{pwd} where id=#{id}")public int updateUser(User user);//delete一个用户@Delete("delete from User where id=#{id}")public int deleteUser(@Param("id") int idd);
}
测试
@Testpublic void test() {SqlSession sqlSession = Utils.getSqlSession();Mapper mapper = sqlSession.getMapper(Mapper.class);List<User> userList = mapper.getUserList();for (User user : userList) {System.out.println(user);}sqlSession.close();}@Testpublic void test1() {SqlSession sqlSession = Utils.getSqlSession();Mapper mapper = sqlSession.getMapper(Mapper.class);User userById = mapper.getUserById(1);System.out.println(userById);sqlSession.commit();sqlSession.close();}@Testpublic void test2() {SqlSession sqlSession = Utils.getSqlSession();Mapper mapper = sqlSession.getMapper(Mapper.class);int user = mapper.addUser(new User(8, "暗之月", "5345345"));System.out.println(user);sqlSession.close();}@Testpublic void test3() {SqlSession sqlSession = Utils.getSqlSession();Mapper mapper = sqlSession.getMapper(Mapper.class);int user = mapper.updateUser(new User(8, "暗", "12312"));System.out.println(user);sqlSession.commit();sqlSession.close();}@Testpublic void test4() {SqlSession sqlSession = Utils.getSqlSession();Mapper mapper = sqlSession.getMapper(Mapper.class);int user = mapper.deleteUser(8);System.out.println(user);sqlSession.commit();sqlSession.close();}
十、Lombok(偷懒,但是不建议使用)
使用步骤:
1.在IDEA中安装Lombok插件
2.在项目中导入Lombok的jar包
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version><scope>provided</scope>
</dependency>
3. 使用注解
@Data:在类上使用,就可以实现,无参构造(隐式)、get、set、toString。
@AllArgsConstructor:有参构造器
@NoArgsConstructor:无参构造器(显式)
还有其他
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {private int id;private String name;private String pwd;}
十一、复杂SQL语句查询
复杂查询环境搭建
-
导入jar包,build,写MyBaits工具类,配置MyBatis核心文件(可以用properties配置连接MySQL的信息)
-
新建实体类Teacher、Student
-
建立对应的Mapper接口
-
建立对应的Mapper.XML文件
-
在核心配置文件中绑定注册我们的Mapper接口或者文件
-
测试查询是否能够成功
多对一处理(多个学生对应一个老师)
按照查询嵌套处理
<select id="getStudents" resultMap="StudentandTeacher">select * from student s</select><resultMap id="StudentandTeacher" type="Student"><result column="id" property="id"/><result column="name" property="name"/><!-- 对象使用association --><association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/><!-- 集合使用collection --><!-- <collection property="" column=""/>--></resultMap><select id="getTeacher" resultType="Teacher">select * from teacher where id=#{tid}</select>
按照结果嵌套处理
<select id="getStudents" resultMap="StudentandTeacher">select s.id sid,s.name sname,t.name tnamefrom student sjoin teacher t on s.tid=t.id;</select><resultMap id="StudentandTeacher" type="Student"><result property="id" column="sid"/><result property="name" column="sname"/><association property="teacher" javaType="Teacher"><result property="name" column="tname"/></association></resultMap>
一对多处理(一个老师拥有多个学生)
按照结果嵌套处理
<select id="getTeacher" resultMap="TeacherandStudent">select s.id sid,s.name sname,t.name tname,t.id tidfrom student sjoin teacher t on s.tid=t.idwhere t.id=#{tid}</select><resultMap id="TeacherandStudent" type="Teacher"><result property="id" column="t.id"/><result property="name" column="tname"/><collection property="students" ofType="Student"><result property="id" column="sid"/><result property="name" column="sname"/><result property="tid" column="tid"/></collection>
按照查询嵌套处理
<select id="getTeacher" resultMap="TeacherandStudent">select * from teacher where id=#{tid}</select><resultMap id="TeacherandStudent" type="Teacher"><result property="id" column="id"/><result property="name" column="name"/><collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent"/></resultMap><select id="getStudent" resultType="Student">select * from student where tid=#{id}</select>
十二、动态SQL
简介
什么是动态SQL?
动态SQL就是根据不同的条件生成不同的SQL语句,就是根据不同条件拼接成不同的SQL语句。
MyBatis的强大特性之一便是它的动态SQL。MyBatis提供了可以被任意SQL映射语句中的强大的动态SQL语言。
搭建环境 (上述)
导包、build、工具类、MyBatis核心配置文件、实体类、Mapper接口、对应XML文件
if
<select id="getBolgList" parameterType="map" resultType="Blog">select *from mybatis.blog<where><if test="title!=null">and title=#{title}</if><if test="author!=null">and author=#{author}</if></where>
对应4种情况
select * from mybatis.blog;(当where中的条件都不符合时,这个where自动省略)
select * from mybatis.blog where title=title;
select * from mybatis.blog where author=author;
select * from mybatis.blog where title=title and author=author;
choose (when,otherwise)
<select id="getBolgListChoose" parameterType="map" resultType="blog">select *from mybatis.blog<where><choose><when test="title!=null">title=#{title}</when><when test="author!=null">and author=#{author}</when><otherwise>and views=#{views}</otherwise></choose></where></select>
对应3种情况
select * from mybatis.blog where title=title;
select * from mybatis.blog where author=author; (当title=null时才执行)
select * from mybatis.blog where views=views; (当title=null and author=null 时才执行)
set
set元素会动态前置SET关键字,同时也会删掉无关的逗号
<update id="updateBolg" parameterType="map">update mybatis.blog<set><if test="title!=null">title = #{title},</if><if test="author!=null">author=#{author}</if></set>where id=#{id}</update>
对应3种情况(不能都为null,否则set部分就缺东西,语法错误)
update mybatis.blog set title=title where id=id;
update mybatis.blog set author=author where id=id;
update mybatis.blog set title=title,author=author where id=id;
trim (where,set) 定制格式
<trim prefix="" prefixOverrides="" suffix="" suffixOverrides="">
foreach
通常在构建 in 条件语句的时候使用
<select id="getBlogIn" resultType="Blog" parameterType="map">select *from mybatis.blog<where>
# collection:map中存在一个集合映射
# item:集合中每个元素名称
# open、separator、close:格式<foreach collection="authors" item="author"open="and (" separator="or" close=")">author=#{author}</foreach></where></select>
SQL片段
有的时候,我们可能会将一些公共功能的部分抽取出来,方便复用。
使用SQL标签抽取公共的部分
<sql id="if-title-author"><if test="title!=null">title=#{title}</if><if test="author!=null">and author=#{author}</if></sql>
在需要使用的地方使用include标签引用
<select id="getBolgList" parameterType="map" resultType="Blog">select *from mybatis.blog<where><include refid="if-title-author"> </include></where></select>
注意事项:
最好基于单表来定义SQL片段,不要存在where标签
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了!!
先在MySQL中写出完整的SQL,再对应的去修改成为我们的动态SQL实现通用即可!!
十三、缓存
简介
什么是缓存?
1.存在内存中的临时数据
2.将用户经常查询的数据放在缓存中,用户去查询数据就不用从磁盘上查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
为什么使用缓存?
减少和数据库的交互次数,较少系统开销,提高系统效率。
什么样的数据能使用缓存?
经常查询并且不经常改变的数据
MyBatis缓存
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存) 二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
一级缓存也叫本地缓存
与数据库同一次会话期间查询到的数据会放在本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。
二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。
基于namespace级别的缓存,一个名称空间,对应一个二级缓存。
要启用全局的二级缓存,只需要在你的SQL映射文件中(对应的MapperXML文件中)添加一行:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
工作机制
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的话,会话关闭了,一级缓存中的数据被保存到二级缓存中;
新的会话查询信息,就可以从二级缓存中获取内容。
总结:
只要开启了二级缓存,在同一个Mapper下就有效。
所有的数据都会放在一级缓存中,只有当会话提交或者关闭的时候,才会提交到二级缓存中。
缓存顺序:先看二级缓存中有没有-->再看一级缓存中有没有-->查询数据库
自定义缓存 Ehcache
Ehcache是一种广泛使用的开源Java分布式缓存,是一个Java缓存框架,提供快速、简单的内存和分布式缓存解决方案,主要面向通用缓存。