掌握MyBatis:增强Java应用的数据层
Mybatis(下)
该文章介绍了SSM中的MyBatis,它不仅允许开发者灵活地编写SQL,优化查询,还能显著提升开发效率。
该文章将清晰地结构化,涵盖MyBatis的基本概念、优势、使用方法以及常见问题,方便你轻松跟随。
此外,我们鼓励读者分享自己的经验和提问,以增加互动性。
一、代码优化
1. Mybatis核心代码中的数据库连接所需的这些属性值,可以通过properties文件外部注入
db.properties
driver=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8username=rootpassword=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.user where 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配置指定该日志的具体实现。
提供了以下这些工厂的实现类
SLF4JLOG4J (掌握)LOG4J2JDK_LOGGINGCOMMONS_LOGGINGSTDOUT_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.ConsoleAppenderlog4j.appender.console.Target = System.outlog4j.appender.console.Threshold=DEBUGlog4j.appender.console.layout = org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=[%c]-%m%n#文件输出的相关设置log4j.appender.file = org.apache.log4j.RollingFileAppenderlog4j.appender.file.File=./log/kuang.loglog4j.appender.file.MaxFileSize=10mblog4j.appender.file.Threshold=DEBUGlog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n#日志输出级别log4j.logger.org.mybatis=DEBUGlog4j.logger.java.sql=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.ResultSet=DEBUGlog4j.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); @Test public 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();}
测试部分不变
@Test public 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构造器解析配置文件流XMLConfigBuilderConfiguration所有的配置信息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);}
测试
@Test public 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(); } @Test public 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(); } @Test public 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(); } @Test public 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(); } @Test public 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@NoArgsConstructorpublic 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 tname from student s join 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 tid from student s join teacher t on s.tid=t.id where 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缓存框架,提供快速、简单的内存和分布式缓存解决方案,主要面向通用缓存。
#Java##Java后端##项目##SSM框架#本专栏汇总了大量的Java面试题和Java面经,希望对大家有所帮助!