掌握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语句查询

复杂查询环境搭建

  1. 导入jar包,build,写MyBaits工具类,配置MyBatis核心文件(可以用properties配置连接MySQL的信息)
  2. 新建实体类Teacher、Student
  3. 建立对应的Mapper接口
  4. 建立对应的Mapper.XML文件
  5. 在核心配置文件中绑定注册我们的Mapper接口或者文件
  6. 测试查询是否能够成功

多对一处理(多个学生对应一个老师)

按照查询嵌套处理

<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面试题和Java面经,希望对大家有所帮助!

全部评论

相关推荐

评论
1
1
分享
牛客网
牛客企业服务