【mybatis】mybatis基础知识总结

1._MyBatis是什么

MyBatis是一个优秀的基于java的持久层框架,它内部封装了JDBC,解决了sql语句不易维护的问题,使开发者只需要关注SQL语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。

MyBatis通过XML或注解的方式将要执行的各种statement配置起来,并通过JAVA对象和statement中SQL的动态参数进行映射生成最终执行的SQL语句。

最后MyBatis框架执行SQL并将结果映射为Java对象并返回采用ORM思想解决了实体和数据库映射的问题,对JDBC进行了封装,屏蔽了JDBC API底层访问细节,使我们不用与JDBC API打交道,就可以完成对数据库的持久化操作

简单说就是:有一种叫MyBatis的持久层技术,能够代替JDBC,简化JDBC开发。

具体技术:

  • Hibernate
  • MyBatis

项目搭建

2._ORM

对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用“虚拟对象数据库”。

简单的说:就是把数据库表和实体类及实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表。

数据库实体类
user表User类
id列id属性
username列userName属性
age列age属性

3._解决包下的UserMapper.xml不会编译(报错)

出现该问题的原因是因为UserMapper.xml作为一个配置文件应该放在java目录下,如果不做特殊配置Maven不会对其进行编译,Maven只会编译java目录下的.java文件。针对这个问题,有两种解决方案:

  1. 在resources目录下新建目录com/qfedu/mapper,将UserMapper.xml剪切到该目录下;
  2. 在pom.xml配置java目录下的xml文件为资源文件,让Maven能够识别,从而进行编译。

我们一般在项目开发中,采用第二种方案进行处理,pom.xml中的新增配置如下:

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                 <!-- src/main/java下面的 任意文件夹及其子文件夹下的所有xml文件 -->
                <include>**/*.xml</include> 
            </includes>
        </resource>
    </resources>
</build>

配置完成后,再次运行测试方法,测试通过。

4._MyBatis映射文件概述(增删改查) => UserMapper.xml

使用

<?xml version="1.0" encoding="UTF-8" ?>
<!-- 映射文件DTD约束,用来约束XML中的标签,直接从素材中拷贝就可以,不需要去记。-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 映射文件的根标签,其他的内容都要写在该标签的内部
    namespace:命名空间,与下面语句的id一起组成查询的标识,唯一的确定要执行的SQL语句 =》 绑定全类名=》就是指这是哪个接口的文件
-->
<mapper namespace="userMapper">
    <!-- select标签表示这是一个查询操作,除了这个标签还可以有insert、delete、update
        id:语句的id,和上面的namespace一起组成查询的标识,唯一的确定要执行的SQL语句
        resultType:查询结果对应的实体类型,目前这里先写全类名(包名+类名)
    -->
    <select id="findAll" resultType="com.qfedu.bean.User">
        <!-- SQL语句 -->
        SELECT * FROM user
    </select>
    
    <!-- 插入操作使用insert标签
        parameterType:指定要插入的数据类型,这里暂时写全类名
    -->
    <insert id="add" parameterType="com.qfedu.bean.User">
        <!-- #{xxx}:使用实体中的xxx属性值 -->
        insert into user(username, password, age, gender, addr) 
        values(#{username}, #{password}, #{age}, #{gender}, #{addr})
    </insert>
    
    <!-- 删除操作使用delete标签
  	  #{任意字符串}方式引用传递的单个参数,如果传入的参数只有一个,大括号中的内容可以随意写,我们最好见名知意 
    -->
    <delete id="delete" parameterType="java.lang.Integer">
        DELETE FROM user WHERE id=#{id}
    </delete>
    
    <update id="chg" parameterType="com.qfedu.bean.User">
        UPDATE user SET username=#{username}, password=#{password} WHERE id=#{id}
    </update>
</mapper>

测试

private SqlSession sqlSession;

@Before
public void init() throws IOException {
    //加载核心配置文件
    InputStream stream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //获得sqlSession工厂对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
    //获得sqlSession对象
    sqlSession = factory.openSession();
}
@After
public void destroy() {
    //释放资源
    sqlSession.close();
}
@Test
public void testDelete() {
     //执行SQL语句
    sqlSession.delete("userMapper.delete", 3);
     //提交事务
    //插入操作涉及数据库的变化,要手动提交事务
    sqlSession.commit();
}

5._MyBatis核心配置文件 => SqlMapConfig.xml

5.1._MyBatis配置文件层次关系

MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息。配置问及那的顶层结构如下

  • configuration (配置)
    • properties(属性)
    • setting(设置)
    • typeAliases(类型别名)
    • typeHandlers(类别处理器)
    • objectFactory(对象工厂)
    • plugin(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource (数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

配置相关标签必须按照上图中的顺序,不然程序无法正常运行

5.2._核心配置文件常用配置解析

<?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>
    <!-- 配置... -->
</configuration>

5.2.1._environments标签

该标签用来配置数据库环境,支持多环境配置

<!-- 配置环境
     default:指定默认环境,如果下面配置了多个id,通过delfault属性的值指定使用哪个环境
-->
<environments default="dev">
    <!-- id:环境的id -->
    <environment id="dev">
        <!-- type:事务管理器类型,类型有如下两种。
            JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
             MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。 
                    默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置
                    为false来阻止它默认的关闭行为。
        -->
        <transactionManager type="JDBC"></transactionManager>
        <!-- type:指定数据源类型,类型有如下三种。
            UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
            POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
            JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,
                容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
         -->
        <dataSource type="POOLED">
            <!-- 配置数据源的基本参数 -->
            <property name="driver" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncpding=utf8" />
            <property name="username" value="root" />
            <property name="password" value="root" />
        </dataSource>
    </environment>
</environments>

5.2.2._mapper标签

该标签的作用是加载映射文件,加载方式有如下几种:

使用相对于类路径的资源引用,例如:

<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>

使用完全限定资源定位符(URL),例如:

<mapper url="file:///var/mappers/AuthorMapper.xml"/>

使用映射器接口实现类的完全限定类名,例如:

<mapper class="org.mybatis.builder.AuthorMapper"/>

将包内的映射器接口实现全部注册为映射器,例如:

<package name="org.mybatis.builder"/>

5.2.3._properties标签

实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件

创建配置数据源的配置文件jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root

修改核心配置文件

<configuration>
<!-- 引入外部配置文件 -->
<properties resource="jdbc.properties" />

<!-- 配置环境 -->
<environments default="dev">
    <environment id="dev">
        <transactionManager type="JDBC"></transactionManager>
        <dataSource type="POOLED">
            <!-- 使用${key}引用properties文件中的值 -->
            <property name="driver" value="${jdbc.driver}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </dataSource>
    </environment>
</environments>
</configuration>

5.2.4._typeAliases标签

5.2.4.1._如何设置别名

配置类型别名,为全类名设置一个短名字

写法1

<!-- 配置别名 -->
<typeAliases>
    <typeAlias type="com.qfedu.bean.User" alias="user" />
</typeAliases>

通过上面的配置为com.qfedu.bean包下的User类起了一个别名user。这种写法需要为每个类分别设置别名,如果类很多,这里的配置会很繁琐。

写法2

<!-- 配置别名 -->
<typeAliases>
    <package name="com.qfedu.bean"/>
</typeAliases>

这种写法为com.qfedu.bean下的所有类设置别名,别名为类名的首字母小写。

5.2.4.2._如何使用别名
<select id="findAll" resultType="user">
    SELECT * FROM user
</select>
<update id="chg" parameterType="user">
    UPDATE user SET username=#{username}, password=#{password} WHERE id=#{id}
</update>
5.2.4.3._MyBatis预先设置好的别名
数据类型别名
java.lang.Stringstring
java.lang.Longlong
java.lang.Integerint
java.lang.Doubledouble
…………
<delete id="delete" parameterType="int">
    DELETE FROM user WHERE id=#{id}
</delete>

6._代理开发方式

代理开发 =》 动态代理 =》 接口的对象由框架生成

MyBatis代理开发方式实现DAO层的开发,这种方式是目前企业的主流。

Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao 接口),由MyBatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

需要遵循的规范:

  • Mapper.xml文件中的namespace与mapper接口的全限定名相同;
  • Mapper接口方法名和Mapper.xml中定义的每个statement的id相同;
  • Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同;
  • Mapper接口方法的返回值类型和mapper.xml中定义的每个sql的resultType的类型相同。

约定大于配置

6.1._关于参数绑定

6.1.1._序号参数绑定

接口

public interface UserMapper {
    User findByNameAndPwd(String username, String password);
}

映射文件

<select id="findByNameAndPwd" resultType="user">
    select * from user where username=#{arg0} and password=#{arg1}
</select><select id="findByNameAndPwd" resultType="user">
    select * from user where username=#{param1} and password=#{param2}
</select>

6.1.2._注解参数绑定(推荐)

接口

public interface UserMapper {
    User findByNameAndPwd(@Param("name") String username, @Param("pwd") String password);
}

映射文件

<select id="findByNameAndPwd" resultType="user">
    select * from user where username=#{name} and password=#{pwd}
</select>

6.1.3._Map参数绑定

接口

public interface UserMapper {
    User findByMap(Map map);
}

映射文件=>map的键名

<select id="findByMap" resultType="user">
    select * from user where username=#{name} and password=#{pwd}
</select>

测试方法

@Test
public void testFindByMap2() {
    HashMap map = new HashMap();
    map.put("name", "admin");
    map.put("pwd", "123456");
    User admin = userMapper.findByMap(map);
    System.out.println(admin);
}

6.1.4._对象参数绑定

接口

public interface UserMapper {
    User fingByObj(User user);
}

映射文件

<select id="fingByObj" resultType="user">
    <!-- 占位符的名字和参数实体类属性的名字相同 -->
    select * from user where username=#{username} and password=#{password}
</select>

测试方法

@Test
public void testFindByObj() {
    User user = new User();
    user.setUsername("admin");
    user.setPassword("123456");

    User u = userMapper.fingByObj(user);
    System.out.println(u);
}

6.2._关于模糊查询

接口

public interface UserMapper {
    List<User> findByName(String name);
}

映射文件

<select id="findByName" resultType="user">
    select * from user where username like concat('%',#{username},'%')
</select>

测试方法

@Test
public void testFindByName() {
    List<User> users = userMapper.findByName("zhang");
    users.forEach(System.out::println);
}

6.3._关于主键回填(返回插入前或者插入后的主键)

6.3.1._通过last_insert_id()查询主键

适用于整数类型自增主键

映射文件

<insert id="add" parameterType="user">
    <selectKey keyColumn="id" keyProperty="id" resultType="long" order="AFTER">
        <!-- 适用于整数类型自增主键 -->
        SELECT LAST_INSERT_ID()
    </selectKey>
    insert into user(username, password, age, gender, addr) values(#{username}, #{password}, #{age}, #{gender}, #{addr})
</insert>

测试方法

@Test
public void testAdd() {
    User user = new User();
    user.setUsername("admin1");
    user.setPassword("123456");

    userMapper.add(user);
    //打印的信息中包含主键
    System.out.println(user);

    sqlSession.commit();
}

6.3.2._通过uuid()查询主键

**适用于字符类型主键 **

接口映射文件

<?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="com.qfedu.mapper.ProductMapper">
    <insert id="add" parameterType="product">
        <selectKey keyProperty="id" keyColumn="id" resultType="string" order="BEFORE">
            <!-- 适用于字符类型主键 -->
            SELECT REPLACE(UUID(),'-','')
        </selectKey>
        insert into product(id, name) values(#{id}, #{name})
    </insert>
</mapper>

测试同LAST_INSERT_ID() ,本接口映射 调用添加方法后,创建的product类中会包含id

7._mybatis映射文件深入

7.1._动态SQL语句if

我们根据实体类属性的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。映射文件如下:

<select id="findByCondition" resultType="user">
    <!-- 注意这里的where 1=1,其实1和true也可以,只要返回的是true就行 -->
    select * from user where 1=1
    <if test="id!=null">
        and id=#{id}
    </if>
    <if test="username!=null">
        and username=#{username}
    </if>
    <if test="password!=null">
        and password=#{password}
    </if>
</select>

如果不想写where 1=1,还有另外一种写法

<select id="findByCondition" resultType="user">
    SELECT * from user
    <where>
        <if test="id!=null">
            and id=#{id}
        </if>
        <if test="username!=null">
            and username=#{username}
        </if>
        <if test="password!=null">
            and password=#{password}
        </if>
    </where>
</select>

对应的Mapper接口

List<User> findByCondition(User user);

测试方法

@Test
public void testFindByCondition() {
    User user = new User();
    user.setId(1);
    //user.setUsername("tom");
    //user.setPassword("123");
    List<User> users = userMapper.findByCondition(user);

    users.forEach(u -> System.out.println(u));
}

运行测试方法,控制台log输出

09:19:48,535 DEBUG findByCondition:159 - ==>  Preparing: SELECT * FROM user WHERE id=? and username=? and password=? 
09:19:48,592 DEBUG findByCondition:159 - ==> Parameters: 1(Integer), tom(String), 123(String)
09:19:48,623 DEBUG findByCondition:159 - <==      Total: 1
User{id=1, username='tom', password='123'}

7.2._动态SQL语句set

我们根据实体类属性的不同取值,使用不同的SQL语句来进行修改。比如在username不为空时对username进行修改,如果password不为空时还要对password进行修改。这种情况在我们的多条件组合查询中经常会碰到。映射文件如下:

<update id="chgByCondition">
    update user
    <set>
        <if test="username != null">
            username=#{username},
        </if>
        <if test="password != null">
            password=#{password},
        </if>
    </set>
    where id=#{id}
</update>

对应的Mapper接口

void chgByCondition(User user);

测试方法

@Test
public void testFindByCondition() {
    User user = new User();
    user.setId(6L);
    user.setPassword("111");

    userMapper.chgByCondition(user);

    sqlSession.commit();
}

7.3._动态SQL语句trim

<trim prefix="" suffix="" prefixOverrides="" suffixOverrides="">`代替`<where><set>
<!--
    prefix:自动加入前缀
    prefixOverrides:自动忽略第一个“and”或者“or”
-->
<select id="findByCondition1" resultType="user">
    select * from user
    <trim prefix="where" prefixOverrides="and|or">
        <if test="username!=null">
            and name like concat('%', #{username}, '%')
        </if>
        <if test="password!=null">
            and password=#{password}
        </if>
    </trim>
</select>

<!--
    prefix:自动加入前缀
    suffixOverrides:自动忽略最后一个”,“
-->
<update id="chgByCondition1" parameterType="user">
    update t_users
    <trim prefix="set" suffixOverrides=",">
        <if test="username!=null">
            name=#{username},
        </if>
        <if test="password!=null">
            password=#{password},
        </if>
    </trim>
    where id=#{id}
</update>

7.4._动态SQL语句foreach

foreach用来循环执行sql的拼接操作,例如:SELECT * FROM user WHERE id IN (1,2,3)

<select id="findByIds" resultType="user">
    SELECT * from user
    <where>
        <!--
            collection:代表要遍历的集合元素,注意编写时不要写#{}
            open:代表语句的开始部分
            close:代表结束部分
            item:代表遍历集合的每个元素,生成的变量名
            sperator:代表分隔符
        -->
        <foreach collection="list" open="id in ("     close=")" separator="," item="id">
            #{id}
        </foreach>
    </where>
</select>

对应的Mapper接口

List<User> findByIds(List<Integer> ids);

测试方法

@Test
public void testFindByIds() {
    List<Integer> ids = Arrays.asList(1, 2);
    List<User> list = userMapper.findByIds(ids);

    list.forEach(u -> System.out.println(u));
}

7.5._SQL片段抽取

目的:将重复的SQL提取出来,使用时用include引用即可,最终达到SQL重用的目的,减少代码冗余。

<?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="com.qfedu.mapper.UserMapper">
    <sql id="selectAll">
        SELECT * FROM user
    </sql>
    <select id="findByIds" resultType="user">
        <include refid="selectAll" />
        <where>
            <foreach collection="list" open="id in (" close=")" separator="," item="id">
                #{id}
            </foreach>
        </where>
    </select>
</mapper>

8._MyBatis多表查询

8.1._一对一查询

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户。

User类Order类OrderMapper接口
public class User {
private Integer id;
private String username;
private String password;
}
public class Order {
private Integer id;
private Date ordertime;
private Double total;
private User user; //代表当前订单从属于哪一个客户,注意这个属性
}
public interface OrderMapper {
List findAll();
}

8.1.1._配置对应的映射文件

OrderMapper.xml

<!--
    resultMap:完成结果映射,表的字段到对象属性的映射,在表的字段名和对象属性名不相同时通常会被用到
    id:设置主键列的对应关系
    result:设置普通列的对应关系
    column:表的字段名
    property:对象的属性名

    这种映射关系了解即可,通常不用这种方式
-->
<!--
    <resultMap id="orderMap" type="order">
        <id column="id" property="id" />
        <result column="ordertime" property="ordertime" />
        <result column="total" property="total" />
        <result column="uid" property="user.id" />
        <result column="username" property="user.username" />
        <result column="password" property="user.password" />
    </resultMap>
-->
 <resultMap id="orderMap" type="order">
     <id column="id" property="id" />
     <result column="ordertime" property="ordertime" />
     <result column="total" property="total" />
     <!-- 
         association:用于建立一对一的关系
          javaType:指定属性的类型
     -->
     <association property="user" javaType="user">
         <id column="uid" property="id" />
         <result column="username" property="username" />
         <result column="password" property="password" />
     </association>
</resultMap>

<!--
     resultMap:用于指定要使用的resultMap
-->
<select id="findAll" resultMap="orderMap">
    SELECT
        o.*, u.id uid, u.username username, u.password password
    FROM
        `order` o, user u
    WHERE
         o.uid=u.id;
</select>

8.1.2._编写测试类

public class Mytest {
    private OrderMapper orderMapper;
    private SqlSession sqlSession;
    @Before
    public void before() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        sqlSession = factory.openSession();

        userMapper = sqlSession.getMapper(UserMapper.class);
        orderMapper = sqlSession.getMapper(OrderMapper.class);
    }

    @After
    public void after() {
        sqlSession.close();
    }

    @Test
    public void test1() {
        List<Order> orders = orderMapper.findAll();
        for (Order order : orders) {
            System.out.println(order);
        }
    }
}

8.1.3._另一种方式

OrderMapper.xml

<?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="com.qfedu.mapper.OrderMapper">
    <resultMap id="orderMap" type="order">
        <id column="id" property="id" />
        <result column="ordertime" property="ordertime" />
        <result column="total" property="total" />
        <!-- 
            association:用于建立一对一的关系
            javaType:指定属性的类型
            column:    数据库中的列名,或者是列的别名,被设置为对应嵌套Select语句的参数
            select:用于加载复杂类型属性的映射语句的 ID,
                它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。
            注意这里select中的写法,这里需要UserMapper.xml中相应ID处有对应的SQL语句
         -->
        <association property="user" column="uid" javaType="user" select="com.qfedu.mapper.UserMapper.findById" />
    </resultMap>

    <select id="findAll" resultMap="orderMap">
        SELECT * FROM `order`
    </select>
</mapper>

UserMapper.xml

<?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="com.qfedu.mapper.UserMapper">
    <select id="findById" resultType="user">
        SELECT * FROM user WHERE id=#{id}
    </select>
</mapper>

8.2._一对多查询

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户。

一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单。

修改User类创建UserMapper接口
public class User {
private Integer id;
private String username;
private String password;
//代表当前用户拥有的多个订单
private List orders;

//set和get方法
//toString方法
}
public interface UserMapper {
List findAll();
}

8.2.1._配置对应的映射文件

<mapper namespace="com.qfedu.mapper.UserMapper">
    <resultMap id="userMap" type="user">
        <id column="id" property="id" />
        <result column="username" property="username" />
        <result column="password" property="password" />
        <!--
             collection:关联一个集合
        -->
        <collection property="orders" ofType="order">
            <id column="oid" property="id" />
            <result column="ordertime" property="ordertime" />
            <result column="total" property="total" />
        </collection>
    </resultMap>

    <select id="findAll" resultMap="userMap">
        SELECT
            u.*, o.id oid, o.ordertime ordertime, o.total total
        FROM
            user u, `order` o
        WHERE
            u.id=o.uid;
    </select>
</mapper>

8.2.2._编写测试类

@Test
public void test2() {
    List<User> users = userMapper.findAll();
    for (User user : users) {
        System.out.println(user);
    }
}

8.2.3._另一种方式

UserMapper.xml

<?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="com.qfedu.mapper.UserMapper">
    <resultMap id="userMap" type="user">
        <id column="id" property="id" />
        <result column="username" property="username" />
        <result column="password" property="password" />
        <!--
             collection:关联一个集合
            column:    数据库中的列名,或者是列的别名,被设置为对应嵌套Select语句的参数
            select:用于加载复杂类型属性的映射语句的 ID,
                它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。
            注意这里select中的写法,这里需要UserMapper.xml中相应ID处有对应的SQL语句
        -->
        <collection property="orders" column="id" ofType="order" select="com.qfedu.mapper.OrderMapper.findByUid" />
    </resultMap>

    <select id="findAll" resultMap="userMap">
        SELECT * FROM user
    </select>
</mapper>

OrderMapper.xml

<?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="com.qfedu.mapper.OrderMapper">
    <select id="findByUid" resultType="order">
        SELECT * FROM `order` WHERE uid=#{id}
    </select>
</mapper>

8.3._多对多查询

用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用。

多对多关系通常需要有第三张表维护两个表之间的关系。

多对多查询的需求:查询用户同时查询出该用户的所有角色。

整个过程和“一对多”查询类似,我们可以把多对多查询理解为双向一对多查询。

SysUser实体类SysRole实体类
public class SysUser {
private Long id;
private String username;
private String email;
private String password;
private String phoneNum;
private List roles;

//set和get方法
//toString方法
}
public class SysRole {
private Long id;
private String roleName;
private String roleDesc;
private List users;

//set和get方法
//toString方法
}
SysUserMapper.javaSysRoleMapper.java
public interface SysUserMapper {
List findAll();
}
public interface SysRoleMapper {
List findAll();
}

8.3.1._创建接口映射文件

SysUserMapper.xml

<?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="com.qfedu.mapper.SysUserMapper">
    <resultMap id="userMap" type="sysuser">
        <id column="id" property="id" />
        <result column="username" property="username" />
        <result column="email" property="email" />
        <result column="password" property="password" />
        <result column="phoneNum" property="phoneNum" />
        <collection property="roles" ofType="sysrole">
            <id column="rid" property="id" />
            <result column="roleName" property="roleName" />
            <result column="roleDesc" property="roleDesc" />
        </collection>
    </resultMap>

    <select id="findAll" resultType="sysuser" resultMap="userMap">
        SELECT
            u.*, r.id rid, r.roleDesc roleDesc, r.roleName roleName
        FROM
            sys_user u, sys_user_role ur, sys_role r
        WHERE
            u.id=ur.userId AND r.id=ur.roleId
    </select>

</mapper>

SysRoleMapper.xml同SysUserMapper.xml,多对多可以理解为两个一对多

8.3.2._编写测试类

@Test
public void test3() {
    List<SysUser> users = sysUserMapper.findAll();
    System.out.println(users);
}

@Test
public void test4() {
    List<SysRole> roles = sysRoleMapper.findAll();
    System.out.println(roles);
}

9._分页-pagehelper

MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。

开发步骤:

  1. 在pom.xml中添加相关依赖;
  2. 在核心配置文件配置PageHelper插件;
  3. 测试。

9.1._在pom.xml中添加相关依赖

<!-- 分页助手 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>3.7.5</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>0.9.1</version>
</dependency>

9.2._在核心配置文件配置PageHelper插件

<!-- 配置插件 -->
<plugins>
    <!-- 配置分页插件 -->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <!-- 配置分页插件方言 -->
        <property name="dialect" value="mysql" />
    </plugin>
</plugins>

9.3._测试

@Test
public void test3() {
    PageHelper.startPage(2, 2);
    List<User> users = userMapper.findAll();

    users.forEach(item -> System.out.println(item));

    PageInfo<Student> pageInfo = new PageInfo<>(users);
    System.out.println("总记录数:" + pageInfo.getTotal());
    System.out.println("当前页:" + pageInfo.getPageNum());
    System.out.println("总页数:" + pageInfo.getPages());
    System.out.println("上一页:" + pageInfo.getPrePage());
    System.out.println("下一页:" + pageInfo.getNextPage());
    System.out.println("是否是首页:" + pageInfo.isIsFirstPage());
    System.out.println("是否是尾页:" + pageInfo.isIsLastPage());
}

10._缓存(Cache)

内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。

10.1._一级缓存

SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。

注意:无需任何配置,默认开启一级缓存。

/**
     * 1、MyBatis一级缓存,一级缓存在SqlSession中
     * 2、如果不关闭Sqlsession,那么SqlSession中的内容会一直存在
     *
     * 如何验证
     *      1.多次查询相同的内容
     *      2.每次查询完成之后不关闭SqlSession
     *      3.通过日志查看发送了几次SQL
     *              |---如果是1次---SqlSession中的缓存是存在的
     */
@Test
public void testLevel1() throws IOException {
    //加载配置文件
    InputStream in = Resources.getResourceAsStream("mybatis_config.xml");
    //创建Session工厂
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    //获取session
    SqlSession session = factory.openSession();

    UsersMapper usersMapper = session.getMapper(UserMapper.class);
    Users u1 = usersMapper.queryById(1);
    System.out.println("---------------------------" + u1);
    Users u2 = usersMapper.queryById(1);
    System.out.println("+++++++++++++++++++++++++++" + u2);

    session.close();
}

10.2._二级缓存

SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中。

注意:在sqlSession.commit()或者sqlSession.close()之后生效。

10.2.1._开启全局缓存

<settings>是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他详细配置可参考官方文档。

<configuration>
    <properties .../>
      <!-- 注意书写位置 -->
    <settings>
        <!-- mybaits-config.xml中开启全局缓存(默认开启) -->
        <setting name="cacheEnabled" value="true"/> 
    </settings>
    
      <typeAliases></typeAliases>
</configuration>

10.2.2._指定Mapper缓存

<mapper namespace="com.qfedu.mapper.UserMapper">
    <cache />
    ...
    ...
</mapper>

代码验证

/**
     * SqlSessionFactory中的缓存是二级缓存,默认不开启
     *
     *  如果开启了二级缓存,如何将查询出的信息存放在二级缓存中?
     *      1、session.commit();
     *      2、session.close;
     *  如何验证二级缓存是否存在
     *      1.多次查询看是否发送了一个SQL语句
     *      2.进行一次查询之后就要commit()或者close连接
     *
     */
@Test
public void testLevel2() throws IOException {
    //加载配置文件
    InputStream in = Resources.getResourceAsStream("mybatis_config.xml");
    //创建Session工厂
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);


    //获取session
    SqlSession session = factory.openSession();
    UsersMapper usersMapper = session.getMapper(UserMapper.class);
    Users u1 = usersMapper.queryById(1);
    System.out.println("------------------" + u1);
    //让二级缓存生效
    session.close();

    session = factory.openSession();
    usersMapper = session.getMapper(UsersMapper.class);
    Users u2 = usersMapper.queryById(1);
    System.out.println("++++++++++++++++++++" + u2);
    session.close();
}
posted @ 2022-04-02 09:45  coderwcb  阅读(8)  评论(0编辑  收藏  举报