SSM框架——MyBatis
阅读原文时间:2023年07月08日阅读:3

Mybatis

1.Mybatis的使用

1.1给项目导入相关依赖

我这里有几个下载好的依赖包提供给大家

点我下载——junit4.13.2

点我下载——maven3.8.1

点我下载——mybatis3.5.7

<!--导入依赖-->
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
    </dependency>

</dependencies>
1.2构建SqlSessionFactory

mybatis文档是这么说的,"每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。 "

用xml文件配置SqlSessionFactory ,在resouses工具类下,创建XML文件并将以下配置文件复制进去

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--环境配置-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;severTimezone=gmt%2b8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
1.3从SqlSessionFactory中获取SqlSession

SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。可以通过 SqlSession 实例来直接执行已映射的 SQL 语句

编写一个工具类去获取sqlSession对象,以后调用即可

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisUtil {
    private static SqlSessionFactory sessionFactory;
    static {
        //获取获取SqlSesson工厂对象
        String resouse = "mybatis-config.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(resouse);
             sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //返回sqlsession对象
    public static SqlSession getSqlSession(){
        return sessionFactory.openSession();
    }
}
1.4编写接口和实现类

一个与数据库表对应的资源类(提供gette、rsetter、构造方法、toString)

package pojo;
public class User {
    private int id;
    private String name;
    public User() {}
    public User(int id, String name) {this.id = id;this.name = name;}
    public int getId() {return id;}
    public void setId(int id) {this.id = id;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

一个有获得结果集的抽象方法的接口

public interface UserDao {
    List<User> getUserList();
}

一个用来映射Sql语句的Mapper文件(提供Session与数据库的对接)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间需要与接口对应-->
<mapper namespace="dao.UserDao">
<!--对应的语句写对应的标签,id为接口的方法,resultType为结果集-->
    <select id="getUserList" resultType="pojo.User">
        select * from jdbcstudy.student;
    </select>
</mapper>
1.5用junit测试
package dao;
import Utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import pojo.User;
import java.util.List;

public class UserTest {
    @org.junit.Test//之前导入了junit的依赖
    public void test(){
        //通过工具类获取Sqlsession对象
        SqlSession session = MyBatisUtil.getSqlSession();
        //用SqlSession的getMappper方法获取实例类
        UserDao userDao = session.getMapper(UserDao.class);
        //调用实例类方法
        List<User> list = userDao.getUserList();
        for (User user : list) {
            System.out.println(user);
        }
        session.closed();
    }
}
1.6 可能会遇到的问题

1.mapper文件未注册

org.apache.ibatis.binding.BindingException: Type interface dao.UserDao is not known to the MapperRegistry.

解决办法:

在配置SqlSessionFactory的xml文件中加入mapper注册

<mappers>
    <mapper resource="dao/UserMapper.xml"/>
</mappers>

2.maven资源输出过滤问题

会报出未初始化和io未找到mapper.xml文件等错误

java.lang.ExceptionInInitializerError

Could not find resource dao/UserMapper.xml

解决办法:在项目配置文件里加入下列代码

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

输出结果:格式不正确可能是没有重写toString方法。楼主我就栽在了这坑里

2.MyBatis操作数据库

mapper标签属性:

namespace:命名空间和接口保持一致

语句标签属性:

id:namespace对应接口中的方法

resultType:返回值类型

需要新的语句时,只需要在接口里新加入抽象方法、以及向mapper映射文件中加入sql语句标签即可

2.1数据库查询(不用事务提交)

eg:根据id查询用户

接口抽象函数:

User getUserById(int id);//根据id查询用户

mapper映射标签:

<select id="getUserById" resultType="pojo.User">
    select * from jdbcstudy.student where id= #{id};
</select>

Test测试:

@org.junit.Test
public void SelectById(){
    SqlSession session = MyBatisUtil.getSqlSession();
    UserMapper userMapper = session.getMapper(UserMapper.class);
    User user= userMapper.getUserById(0);
    System.out.println(user);
}
2.2数据库增删改(需要提交事务)
@org.junit.Test
public void addUser(){
    SqlSession session = MyBatisUtil.getSqlSession();
    UserMapper userMapper = session.getMapper(UserMapper.class);
    int add = userMapper.addUser(new User(2,"赵中波"));
    if(add>0) System.out.println("修改成功");
    //事务提交
    session.commit();
    session.close();
}
2.3Map传参

使用map传参要比基本类型传参和对象传参更加灵活,可以使用key值自定义属性名,在用value存储值,eg:

<insert id="addUser" parameterType="map">
    insert into jdbcstudy.student (id,name) values (#{idkey},#{namekey});
</insert>
2.4模糊查询

一般数据库里模糊查询是使用%,比如查询李姓用户 like '李%'

myBatis里可以用字符串拼接,或是在传参时传入参数为("李%"),但这样就显得很low或是接口设计不良好,一般是在sql语句映射文件里用字符串拼接

<select id="getUserById" parameterType="int" resultType="pojo.User">
    select * from jdbcstudy.student where id= #{id}"%";
</select>
或者是
<select id="getUserById" parameterType="int" resultType="pojo.User">
    select * from jdbcstudy.student where id= concat(#{id},"%");
</select>

3.MaBatis配置解析

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下(配置的时候也要按这个顺序来):

3.1环境配置

1.事务管理器(transactionManager)

类型:jdbc(默认)、managed

2.数据源(datasourse)

类型:Pooled(默认,连接池)、Unpooled、Jdni(正常连接)

3.属性(properties)

属性可以在外部进行配置.properties文件,并可以进行动态替换。也可以在 properties 元素的子元素中设置。

优先级:类型传参>外部文件>子元素设置

4.别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写,这样配置时,别名可以在任何地方替代原类

<typeAliases>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
</typeAliases>

比较实用的是用别名扫描一个包,比如原先我定位到一个累要用com.#.@.%.User,加入包扫描后就可以直接用user(首字母小写)定位该类

<typeAliases>
<package name="com.#.@.%"/>
</typeAliases>

5.映射器(mappers)

每个mapper映射器在使用前都需要注册

<mappers>
    <mapper resource="dao/UserMapper.xml"/>使用相对路径
    <mapper url="D://UserMapper.xml"/>使用绝对路径
    <mapper class="dao.UserMapper"/>使用类导入方式(mapper文件和该接口类同包且同名)
    <package name="dao"/>使用包导入方式(接口类和mapper文件同名)
</mappers>

4.结果集映射

使用返回实体类类型的方式,如果类属性和数据库列名匹配不上(不同名)的话,就会出现查询出null的情况,为了解决这个问题,可以使用结果集映射

<resultMap id="name" type="class">
 <result column="数据库列名" property="类属性名"/>
 <!--如果属性是对象,一般需要通过某个列名再去查询到该对象(多对一查询)-->
 <association column="映射对象的列名" property="对象属性名" javaType="对象类型" select="连接查询的id">
     <result column="数据库列名" property="类属性名"/><!--对象字段里也可以进行结果映射-->
 </association>
    <!--如果属性是集合,一般需要通过某个列名再去查询到该集合(一对多查询)-->
 <collection property="对象属性名" column="映射集合的列名" javaType="集合类型" ofType="集合泛型类型" select="连接查询的id">
     <result column="数据库列名" property="类属性名"/><!--集合字段里也可以进行结果映射-->
 </collection>
</resultMap>

使用方法(resultMap="结果集映射的id"):

<select id="getUserList" resultMap="name">
 select * from jdbcstudy.student;
</select>

5.日志工厂

logImpl

指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

SLF4J | LOG4J | LOG4J2 | JDK_LOGGING

COMMONS_LOGGING | STDOUT_LOGGING

NO_LOGGING

设置方式(设置标准日志STDOUT_LOGGING)

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

日志打印

5.1 LOG4J日志工厂

导入log4j

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

配置log4j:log4j.properties(下面是一个简单的配置)

# set log levels
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/leye.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

使用:Logger logger = Logger.getLogger(class);

6.分页

limit分页,使用了map之后可以传多个参数

<select id="getUserList" parameterType="map" resultType="pojo.User">
    select * from jdbcstudy.student limit #{startpage},#{num};
</select>

7.使用注解开发

7.1通过注解注入sql语句

将sql语句直接注解到接口抽象方法里,比如一个简单的查询语句

@Select("select * from student")
List<User> getUserList();//无参查询

但是,注解注入的方式只适用于少数sql语句比较简单的情况,值得一提的是,在对于多个基本类型参数sql方法,必须要在参数前再加一个指名注解

User getUserBy(@Param("id") int id ,@Param("name") String name);
7.1注解解决列名和属性名不匹配问题

之前是用map解决的,但对于只有少数参数的sql语句,用map就有点大材小用,这里用注解更加方便,但只针对于用注解注入sql语句的情况

@Select("select * from student")
    @Results(id = "userMap",value = {
            @Result(column = "id" ,property = "anInt")
    })
    List<User> getUserList();//无参查询

设置id是为了方便其他的结果映射注解去调用,如

@Select("select * from student where id=#{id}")
@ResultMap(value = "userMap")
User getUserById(int id);//根据id查询用户

8.动态SQL

8.1 IF标签
<select id="" parameterType="" resultType="">
 select * from student where 1=1<!--方便拼接字符串-->
    <if test="表达式">
        and sql语句<!--表达式成立的话,拼接该字符串-->
    </if>
</select>
8.2 where标签

一般为了在if等标签里拼接字符串,前面都得模拟一个true的where语句

<select id="" ..>
 select * from student where 1=1
 <if>...</if>
</select>

这样虽然可行,但却降低了耦合性

jsp标签提供了一个where标签来解决这种问题

注意:在第二条语句之后都要加上and

<select id="" ..>
 select * from student
 <where>
     <if test="">...</if>
     <if test="">and...</if>
 </where>
</select>
8.3choose——when语句

类似于java的swicth

<choose>
 <when test="表达式">
     and 拼接sql
 </when>
 ...
 <otherwise>
     ..
 </otherwise>
</choose>
8.4 set语句

set语句可以自动联系sql语句中的 ','逗号,发现多余的逗号会帮你去掉,和where标签的and比较像,多用于像修改、插入这种含,的sql语句

<set>
    <if test="">...,</if>
    <if test="">...,</if>
    <if test="">...,</if>
</ set>
8.5 trim标签定制语句覆盖

像where、set标签都可以通过trim标签去定制

<trim prefix="where" prefixOverrides="AND|OR">//自动覆盖and、or前缀
 //定制where语句
</trim>

<trim prefix="set" suffixOverrides=",">//自动覆盖,后缀
 //定制set语句
</trim>
8.6 Sql片段

可以将某些sql的公共部分提取出来,以便复用

//sql片段定义
<sql id="sqlName">
 ...
</sql>
//sql片段使用
<include refid="sqlName"></include>
8.7 forEach标签

可以遍历取出list里的元素拼接sql语句

<foreach collection="listName" item="elemName" open="" close="" separator="">
 ...
</foreach>
//其中collection是要从中取出元素的集合,item是每个元素的引用名称,
open、close分别是拼接sql的开始和结束,sepqarator是拼接sql的间隔符
比如要拼接一个(1,2,3,4)的sql
<foreach collection="listName" item="id" open="(" close=")" separator=",">
 #{id}
</foreach>

9.MyBatis缓存

MyBatis默认定义了二级缓存:一级缓存和二级缓存

<setting name="cacheEnabled" value="true">//开启缓存

一级缓存:SqlSession级别的缓存,也就是本地缓存,增删改查和不同查询等会自动清除缓存

二级缓存:基于namespace的缓存,需要手动开启配置,它可以将对话框关闭后存在一级缓存中的数据存到mapper对应的二级缓存中

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" >
    //创建了一个每60s刷新的FIFO缓存,最多可以存储512个引用,而且返回只读对象