首页 > 其他分享 >MyBatis

MyBatis

时间:2022-10-02 13:24:36浏览次数:57  
标签:mapper 缓存 查询 sqlSession emp MyBatis id

MyBatis

1、搭建MyBatis

1.1环境配置与工程

<dependencies>
    <!-- Mybatis核心 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>

    <!-- junit测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
    </dependency>
    
    <!-- log4j日志 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

</dependencies>

注:找好数据库版本号:

image

1.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>
<!--设置连接数据库的环境-->
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/ssm?
serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>
<!--引入映射文件-->
<mappers>
    <package name="mappers/UserMapper.xml"/>
</mappers>
</configuration>

1.3mapper接口对应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.zylai.mybatis.mapper.UserMapper">

    <!--void updateUser()-->
    <insert id="insertUser">
        insert into t_user values(null,'admin','123456',23,'男','[email protected]')
    </insert>
    
</mapper>

1.4测试类

import com.atguigu.mybatis.mapper.UserMapper;
import com.atguigu.mybatis.pojo.User;
import com.atguigu.mybatis.utils.SqlSessionUtil;
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 org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class test {
    @Test
    public void testInsert() throws IOException {
        //获取核心配置文件的输入流
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        //获取SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //获取SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        //获取sql的绘画对象,是Mybatis提供的操作数据库的对象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        //获取UserMapper的代理实现类对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //调用mapper接口中的方法,实现添加用户信息

        int result = mapper.insertUser();
        System.out.println("结果:" + result);
        sqlSession.close();
        //注意:这里使用Mapper接口的代理对象其实是最终的一种方法,通过代理对象重写接口的方法
        //本质上,就是调用了接口的方法,实现类中使用SqlSession的方法
        // 提供sql的唯一标识找到SQL并执行,唯一标识是namespace.sqlId。
        // 这里就不需要用到接口了,接口的名称和方法只是作为唯一id,完全可以换成其他的。
//        int res1 = sqlSession.insert("com.zylai.mybatis.mapper.UserMapper.insertUser");
//        System.out.println("结果:"+res1);

        /*
        总结:
        1.创建mapper接口的代理对象
        2.代理对象重写了mapper接口中的方法
        3.执行SqlSession的方法,参数是sql的唯一标识
        4.返回结果
         */

        //7.提交事务,使用空参的SqlSession,需要自己进行事务的控制
        //sqlSession.commit();
        //8.关闭会话
    }

    @Test
    public void testUpdate() {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        int result = userMapper.updateUser();
        System.out.println("结果:" + result);
        sqlSession.close();
    }

    @Test
    public void testGetUserById() {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.getUserById();
        System.out.println("结果:" + user);
        sqlSession.close();
    }

    @Test
    public void testGetAllUser() {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = userMapper.getAllUser();
        list.forEach(System.out::println);
        sqlSession.close();
    }
}

1.4.1原理

通过Mapper接口的代理对象其实是执行这个SQL的最终方法,其本质上就是代理对象实现了接口中的方法,会实际执行SqlSession的具体方法,比如:sqlSession.insert("com.zylai.mybatis.mapper.UserMapper.insertUser");。此类方法根据SQL的唯一标识找到SQL并执行,唯一标识是namespace.sqlId,即mapper方法对应的全类名加上方法名。因此,他会去获取当前方法的全类名和方法名作为参数传入上述方法中。

因此如果不使用mapper接口的代理对象,采用如下的方式,其本质上是完全一样的
这里就不需要用到接口了,接口的名称和方法只是作为唯一id,完全可以换成其他的。

int res1 = sqlSession.insert("com.zylai.mybatis.mapper.UserMapper.insertUser");

System.out.println("结果:"+res1);

总结:

  1. 创建mapper接口的代理对象
  2. 代理对象重写了mapper接口中的方法
  3. 执行SqlSession的方法,参数是sql的唯一标识

1.5增删改查(完整)

1.5.1mapper接口

public interface UserMapper {
    //增
    int insertUser();

    //改
    void updateUser();

    //删
    void deleteUser();

    //根据id查
    User getUserById();

    //查询所有的用户信息
    List<User> getAllUser();
}

User类各属性应与数据库属性名对应,必须有无参构造器,最好有有参构造器和toString方法,做好get set封装

1.5.2XML映射文件

<?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.atguigu.mybatis.mapper.UserMapper">
    <!--
        mapper接口和映射文件要保证一致
        1.mapper接口的全类名和映射文件的namespace一致
        2.mapper接口的方法的方法名要和映射文件的sql的id保持一致

    -->

    <!-- int insertUser();  -->
    <insert id="insertUser">
        insert into t_user values(null,'admin','123456',23,'男','[email protected]')
    </insert>
    <update id="updateUser">
        update t_user set email = '[email protected]'	where id = 1
    </update>
    <delete id="deleteUser">
       	delete from t_user where id = 3
    </delete>


    <!--
        resultType:设置结果类型,即查询的数据要转换为的java类型
        resultMap:自定义映射,处理多对一或者一对多的元素关系
        二者只能设置一个
    -->
    <select id="getUserById" resultType="com.atguigu.mybatis.pojo.User">
       	select * from t_user where id = 1
    </select>

    <select id="getAllUser" resultType="abc">
       	select * from t_user
    </select>

</mapper>

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">被标红,则Alt+Enter,选择第一个导入外部资源

1.6mybatis核心配置

1.配置顺序 必须按照指定顺序配置
2.properties标签 用于访问配置文件
3.typeAliases标签 类型别名(三种方式,某个类指定别名,某个类默认别名,包中所以默认别名)
4.environment标签 environoments environment transactionManager dataSource标签,包括environoments中的default属性设置默认环境
5.mappers

1.6.1配置顺序

MyBatis核心配置文件中的标签必须要按照指定的顺序配置:

(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,plugins?,environments?,
databaseIdProvider?,mappers?)

1.6.2properties标签

引入properties文件,此后就可以在当前文件中使用${key}的方式来访问value

<!--引入properties文件,此后就可以在当前文件中使用${key}的方式来访问value-->
<properties resource="jdbc.properties"/>

面试题: #{} 和 ${} 的区别是什么?

  • ${}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc. Driver
  • #{}是 sql 的参数占位符,MyBatis 会将 sql 中的#{}替换为? 号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的? 号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name} 的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 param.getItem().getName()

1.6.3typeAliases标签

采用通过包名设置别名,该包下所有的类都有默认的别名,不区分大小写。

<!--
    typeAliases:设置类型别名,为某个具体的类型设置一个别名
    在Mybatis的范围中,就可以使用别名表示一个具体的类型
-->
<typeAliases>
    <!--
        type:设置需要起别名的类型
        alias:设置某个类型的别名
    -->
    <!--<typeAlias type="com.zylai.mybatis.pojo.User" alias="abc"/>-->
    <!--如果不设置alias属性,那么默认的别名就是类名且不区分大小写-->
    <!--<typeAlias type="com.zylai.mybatis.pojo.User"/>-->

    <!--通过包来设置类型别名,指定包下所有的类型将全部拥有默认的别名,即类名且不区分大小写-->
    <package name="com.zylai.mybatis.pojo"/>
</typeAliases>

1.6.4environment标签

<!--    配置连接数据库的环境-->
<!--
        environments:配置连接数据库的环境
        属性:
            default:指定默认使用的环境,比如选择开发和测试
-->
    <environments default="development">
<!--        environment:设置一个具体的连接数据库的环境
            属性:
            id:设置环境的唯一标识,不能重复
-->
        <environment id="development">
            <!--
               transactionManager:设置事务管理器
               属性:
               type:设置事务管理的方式。JDBC或MANAGED
               JDBC:表示使用JDBC中原生的事务管理方式
               MANAGED: 被管理,例如Spring
            -->
            <transactionManager type="JDBC"/>
            <!--
            dataSource:设置数据源
            属性:
             type:设置数据源的类型;
             值:POOLED,UNPOOLED,JNDI
             POOLED:表示使用数据库连接池
             UNPOOLED:不使用数据库连接池,每一次获取连接时都重新创建连接
             JNDI:(了解)表示使用上下文中的数据源
            -->
            <dataSource type="POOLED">
                <!--注意,这里是mysql8.0的配置,如果是mysql5,就是com.mysql.jdbc.Driver-->
                <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>

        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--注意,这里是mysql8.0的配置,如果是mysql5,就是com.mysql.jdbc.Driver-->
                <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>

1.6.5mappers标签

这里引入mybatis的映射文件,映射文件中是操作数据库的SQL语句,需要在这里指定xml文件来引入。

为了方便,可以指定映射文件的包名,然后满足这两个条件:

<!--    引入mybatis的映射文件,映射文件中是操作数据库的SQL语句,需要通过
当前的核心配置文件引入映射文件-->
    <mappers>
        <!--<mapper resource="mappers/UserMapper.xml"/>-->
        <!--
        相当于typeAliases给mapper起别名
            以包的方式引入映射文件,但是必须满足两个条件:
             1.mapper接口和映射文件所在的包必须一致
             2.mapper接口的名字和映射文件的名字必须一致
        -->
        <package name="com.zylai.mybatis.mapper"/>
    </mappers>
  1. mapper接口和映射文件所在的包必须一致

    注意:映射文件一定是路径结构比如像com\zylai\mybatis\mapper,对应接口所在的包com.zylai.mybatis.mapper,这样一来mapper映射文件和mapper接口的类加载之后会在同一个目录下,所以直接通过文件名称和接口类的名字就可以匹配

  2. mapper接口的名字和映射文件的名字必须一致

总览核心配置文件内容

<?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>
    <!--
        Mybatis核心配置文件中,标签必须按照指定的顺序配置!!!
        properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
        objectWrapperFactory?,reflectorFactory?,plugins?,environments?,
        databaseIdProvider?,mappers?
      -->

    <!-- 引入properties文件,此后就可以在当前文件中使用$(key)的方式访问value  -->
    <properties resource="jdbc.properties" />

    <!--
        类型别名   User类 就可以用 abc表示-
        typeAliases:设置类型别名,即为某个具体的类型设置一个别名
        在MyBatis的范围中,就可以使用别名表示一个具体的类型
    -->
    <typeAliases>
        <!--
            type 设置需要起别名的类型
            alias 设置某个类型的别名
            方法二:
            <typeAlias type="com.atguigu.mybatis.pojo.User" ></typeAlias>
            不设置alias时,默认类名为别名,且不区分大小写,即User
            方法三:
            <package name="com.atguigu.mybatis.pojo"/>
            通过包设置类型别名,然后指定包下所有类型将全部拥有默认别名,且不区分大小写
        -->

        <typeAlias type="com.atguigu.mybatis.pojo.User" alias="abc"></typeAlias>
    </typeAliases>

    <!--    配置链接数据库的环境-->
    <!--        属性  default:设置默认使用环境的id         -->
    <environments default="development">
        <!--        id设置环境的唯一标识,不能重复         -->
        <environment id="development">
            <!--
                transactionManager:设置事务管理器
                属性:
                    type:设置 事务管理的方式
                    type="JDBC|MANAGED"
                    JDBC:表示使用JDBC中原声的事务管理方式
                    MANAGED:被管理,例如 spring
            -->
            <transactionManager type="JDBC"/>
            <!--
                dataSource :设置数据源
                属性:
                    type:设置数据源类型
                    type="POOLED|UNPOOLED|JNDI"
                    POOLED:     表示使用数据库连接池
                    UNPOOLED:   表示不使用数据库连接池
                    JNDI:       表示使用上下文的数据源
            -->
            <dataSource type="POOLED">
                <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>

    <!--引入mybatis的映射文件-->
    <mappers>
        <!--方法一:引入单个mapper接口        -->
        <!--        <mapper resource="mappers/UserMapper.xml"/>-->

        <!--方法二:以报的方式引入映射文件,但是必须满足两个条件
                    1.mapper接口和映射文件所在的包必须一致
                    2.mapper接口的名字和映射文件的名字必须一致
        -->

        <package name="com.atguigu.mybatis.mapper"/>
    </mappers>
</configuration>

1.6.7核心配置与映射文件模板

File→setting→Editor→File and Code Templates

在Files中添加,记得更改模板名以及文件后缀名(xml)

2、MyBatis获取参数

2.1获取参数的两种方式#{}${}

#{}:会自动加上一对单引号,使用较多

${}:不会加上单引号,需要我们手动在SQL中为参数加上单引号,所以一般使用较少,不过在一些特殊的查询里会用到

#{}本质是占位符赋值${}本质是字符串拼接

<select id="getUserByUsername" resultType="User">
    select * from t_user where username=#{username}
</select>

<select id="getUserByUsername" resultType="User">
    select * from t_user where username='${username}'
</select>

2.2 mapper方法的参数为单个的字面量类型

此时#{}${}以任意内容获取参数值(不过一般使用参数名),一定注意 ${}的单引号问题。

<!--方法参数为单个的字面量类型,#是占位符方式,里面的内容可以随便写。$是字符串拼接,需要手动加单引号-->
<select id="getUserByUsername" resultType="User">
    select * from t_user where username=#{username}
</select>

2.3mapper方法的参数为多个的字面量类型

此时mybatis会将参数放在map集合中,以两种方式存储数据

  • 以arg0,arg1…为键,以参数为值

  • 以param1,param2,…为键,以参数为值

    因此,只需要通过#{}访问map的键来获取map的值。也可${},注意单引号

<!--    User checkLogin(String username,String password);-->
    <select id="checkLogin" resultType="User">
        select * from t_user where username=#{param1} and password = #{param2}
    </select>

下面省略说明${},都可以用${}实现,且注意加单引号

2.4mapper方法的参数为一个map集合类型的参数

只需要通过#{}访问map集合的键来获取map的值。

<!--    User checkLoginByMap(Map<String,Object> map);-->
    <select id="checkLoginByMap" resultType="User" >
        select * from t_user where username=#{username} and password = #{password}
    </select>

2.5mapper方法的参数为实体类类型

只需要通过#{}访问实体类中的属性名,就可以获取响应的属性值

<!--    void insertUser(User user);-->
    <insert id="insertUser">
        insert into t_user values(null,#{username},#{password},#{age},#{gender},#{email})
    </insert>

2.6 在mapper接口方法的参数上设置@param注解⭐

此时Mybatis会将这些参数放在map中,以两种方式进行存储

  1. 以@Param注解的value属性值为键,以参数为值
  2. 以param1,param2…为键,以参数为值
//使用注解,其中的value=?可以省略不写
User checkLoginByParam(@Param("username") String username, @Param("password") String password);
<!--    User checkLoginByParam(@Param("username") String username, @Param("password") String password);-->
    <select id="checkLoginByParam" resultType="User">
        select * from t_user where username=#{username} and password = #{password}
    </select>

2.7 总结

真正使用的就是两种情况:

  1. 使用注解(包括了情况2,3,4,6 这些情况下建议都用注解)
  2. 使用实体类对象,直接通过实体类属性获取值就可

3、查询

3.1 查询一个实体类对象

如果返回值只有一条,底层调用的是selectOne方法。如果返回多条,就会报错

/**
     * 若sql语句查询的结果为多条时,一定不能以实体类类型作为方法的返回值
     * 否则会抛出异常TooManyResultsException,因为底层调用sqlSession.selectOne方法
     *
     */
    //根据id查询用户信息
    User getUserById(@Param("id") Integer id);
<!--    User getUserById(@Param("id") Integer id);-->
    <select id="getUserById" resultType="User">
        select * from t_user where id = #{id}
    </select>

测试类:

@Test
public void testGetUserByUsername() {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserByUsername("admin1");
    System.out.println(user);
    sqlSession.close();
}

3.2 查询一个集合(实体类对象集合)

在xml文件中返回值类型选择集合对应的泛型即可

//查询所有的用户信息
List<User> getAllUser();
<!--    List<User> getAllUser();-->
    <select id="getAllUser" resultType="User">
        select * from t_user
    </select>

测试类:

    @Test
    public void testGetAllUser() {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.getAllUser();
        for (User user : users) {
            System.out.println(user);
        }
        sqlSession.close();
    }

3.3 查询为单个数据(统计)

//查询用户的总数量
Integer getCount();
<!--    Integer getCount();-->
<!--    这里可以直接使用Integer或int,且不区分大小写,不用写全类名
        MyBatis中为java中常用的类型设置了类型别名
        Integer:integer,int
        int: _int
        Map: map
        String: string
-->
    <select id="getCount" resultType="Integer">
        select count(*) from t_user
    </select>

测试类:

    @Test
    public void testGetCount() {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Integer count = mapper.getCount();
        System.out.println(count);
        sqlSession.close();
    }

mybatis中常用类型别名

image-20220721145354407

image

3.4 查询一条(不能转为实体类对象的)数据,返回map集合

如果查询结果不是一个实体类对象的情况

//根据id查询用户信息为map集合
Map<String,Object> getUserByIdToMap(@Param("id") Integer id);
<!--    Map<String,Object> getUserByIdToMap(@Param("id") Integer id);-->
    <select id="getUserByIdToMap" resultType="map">
        select * from t_user where id = #{id}
    </select>

测试类:

    @Test
    public void testGetUserByIdToMap() {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> userByIdToMap = mapper.getUserByIdToMap(2);
        for (String key : userByIdToMap.keySet()) {
            System.out.println(key + " = " + userByIdToMap.get(key));
        }
        sqlSession.close();
    }

map的遍历方法:

1.foreach使用keySet()方法,遍历key
//Integer为键的数据类型
for(Integer key:map.keySet()){
  //通过key,找到对应的value
       System.out.println("key:"+key+" "+"Value:"+map.get(key));
        }
2.把所有的键值对装入迭代器中,然后遍历迭代器
 Iterator<Map.Entry<Integer,String>> it=map.entrySet().iterator();
      while(it.hasNext()){
          Map.Entry<Integer,String> entry=it.next();
          System.out.println("key:"+entry.getKey()+" "
                  +"Value:"+entry.getValue());
      }
3.小结

其实主要就两种方法,

第一种是通过keySet()方法,获得key,然后再通过map.get(key)方法,把参数key放入即可得到值;

第二种是先转为为Set类型,用entrySet()方法,其中set中的每一个元素值就是map的一个键值对,也就是Map.Entry<K,V>,然后就可以遍历了。如果只需要得到map的值,那直接调用map.getValue()方法就可以了。

3.5 查询多条(不能转为实体类对象的)数据map集合

两种方案

3.5.1 List封装多条map

*  将mapper接口方法的返回值设置为泛型时map的list集合
* List<Map<String,Object>>
* 最终的结果:
*{password=123456, gender=男, id=1, age=23, [email protected], username=admin1}
* {password=123456, gender=男, id=2, age=23, [email protected], username=admin}
* {password=123456, gender=男, id=4, age=23, [email protected], username=admin}
* {password=123, gender=女, id=5, age=33, [email protected], username=xiaoming}
* {password=123, gender=女, id=7, age=33, [email protected], username=xiaoming}
* {password=123, id=8, username=jack}
* {id=9}
 //查询所有的用户信息为map集合,这里需要使用list集合去存储map
    List<Map<String,Object>> getAllUserToMapList();
<!--    List<Map<String,Object>> getAllUserToMapList();-->
    <select id="getAllUserToMapList" resultType="map">
        select * from t_user
    </select>

测试类(拆解或者说遍历List<Map<String, Object>>):

    @Test
    public void testGetAllUserToMapList() {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //拆解List<Map<String, Object>>
        List<Map<String, Object>> allUserToMapList = mapper.getAllUserToMapList();
        for (Map<String, Object> map:allUserToMapList){
            for (String key : map.keySet()) {
                System.out.println(key + " = " + map.get(key));
            }
            System.out.println("");
        }
        sqlSession.close();
    }

3.5.2 map封装多条map

* 2.将每条数据转换的map集合放在一个大的map集合中,但是必须通过@MapKey注解
* 将查询的某个字段的值作为大的map的键
*  @MapKey("id")
*  Map<String, Object>
* 结果:
* {
* 1={password=123456, gender=男, id=1, age=23, [email protected], username=admin1},
* 2={password=123456, gender=男, id=2, age=23, [email protected], username=admin},
* 4={password=123456, gender=男, id=4, age=23, [email protected], username=admin},
* 5={password=123, gender=女, id=5, age=33, [email protected], username=xiaoming},
* 7={password=123, gender=女, id=7, age=33, [email protected], username=xiaoming},
* 8={password=123, id=8, username=jack},
* 9={id=9}
* }
    //将查询出来的值放到map中,再把这些map放到一个大的map中,这个大的map的键通过注解指定为id
    @MapKey("id")
    Map<String,Object> getAllUserToMap();
<!--@MapKey("id")-->
<!--Map<String,Object> getAllUserToMap();-->
<select id="getAllUserToMap" resultType="map">
    select * from t_user
</select>

测试类(拆解或者说遍历 类似Map<id,Map<String, Object>>):

    @Test
    public void testGetAllUserToMap() {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //拆解或者说遍历 类似Map<id,Map<String, Object>>
        Map<String, Object> allUserToMap = mapper.getAllUserToMap();
        System.out.println(allUserToMap);
        sqlSession.close();
    }
@MapKey注解
1.使用场景

MyBatis查询一些记录,数据涉及到两个表里的数据,需要连表查询,但我MyBatis的返回结果不想创建新的DO对象,因此使用@MapKey注解返回一个Map集合。

2.含义

@MapKey注解用于mapper.xml文件中,一般用于查询多条记录中各个字段的结果,存储在Map中。Map结构的示例如下:

Map<Long, Map<String, String>>//范型类型可以修改

Map的key:一般存储每条记录的主键,也可以用其他值表示,主要取决于Dao层@MapKey注解后面的字段(如@MapKey("id"));
Map的value:也是一个Map,表示查询出这条记录的每个字段的字段名称和字段值。

4、特殊SQL的执行

4.1 模糊查询

#{}会自动加上一个单引号,${}不会。包括4.2,4.3

SQL关键字:like

_ 任意单个字符;% 任意个数的任意字符

//模糊查询用户名
List<User> getUserByLike(@Param("keyword") String keyword);
<!--List<User> getUserByLike(@Param("keyword") String keyword);-->
<!--在单引号中占位符会被解析为字符串-->
<select id="getUserByLike" resultType="User">
    select * from t_user where username like "%"#{keyword}"%"  
</select>

三种写法:

select * from t_user where username like '%${keyword}%'
select * from t_user where username like concat('%',#{keyword},'%')
select * from t_user where username like "%"#{keyword}"%"  

第三种最常用,建议使用

4.2 批量删除

这里只能使用${},因为使用#{}会自动加上一个单引号出现错误。 所以使用${}不会自动加上单引号,不使用占位符赋值

//批量删除
void deleteMoreUser(@Param("ids") String ids);
<!--void deleteMoreUser(@Param("ids") String ids);-->
<!--这里只能使用${},因为使用#{}会自动加上一个单引号出现错误。
    所以使用${}不会自动加上单引号,不使用占位符赋值-->
<delete id="deleteMoreUser">
    delete from t_user where id in(${ids})
</delete>

SQL关键字 IN

IN( , , , , )

4.3 动态设置表名,查询当前用户信息

//动态设置表名,查询用户信息
List<User> getUserList(@Param("tableName") String tableName);
<!--List<User> getUserList(@Param("tableName") String tableName);-->
<!--同上,这里也不能用#{},因为表名不能使用单引号-->
<select id="getUserList" resultType="User">
    select * from ${tableName}
</select>

4.4 添加用户信息获取主键

不能把主键值作为返回值是因为增删改的返回值固定为影响行数,所以把获取的主键放到传入对象User的指定属性keyProperty中。

  • useGeneratedKeys:表示添加功能使用了自增的主键
  • keyProperty:指定将主键值赋值给实体类的参数
//添加用户信息并获取主键
void insertUser(User user);
<!--void insertUser(User user);-->
<!--不能把主键值作为返回值是因为增删改的返回值固定为影响行数
    所以把获取的主键放到传入对象User的指定属性keyProperty中

    useGeneratedKeys:表示添加功能使用了自增的主键
    keyProperty:指定将主键值赋值给实体类的参数
    -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
    insert into t_user values(null,#{username},#{password},#{age},#{gender},#{email})
</insert>

5、自定义映射resultMap

5.1 处理字段和属性的映射关系

对于字段名和属性名不一致的情况,如何处理映射关系(三种方式)

1.为查询的字段设置别名,和属性名保持一致 (一般不用)
<select id="getEmpByEmpId" resultType="Emp">
    select emp_id empId,emp_name empName,age,gender from t_emp where emp_id = #{empId}
</select>
2.当字段符合mysql的要求使用_,而属性符合java要求使用驼峰

此时可以在mybatis的核心配置文件中设置一个全局配置,可以自动将下划线映射为驼峰

emp_id empId

emp_name empName

mybatis-config.xml核心配置文件

<!--设置将下划线映射为驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
3.使用resultMap自定义处理映射

resultMap:自定义的映射关系
id:唯一标识
type:处理映射关系的实体类的类型
使用了resultMap就需要把每个映射关系都写出来

常用的标签:

标签 功能
id 处理主键和实体类中属性的映射关系
result 处理普通字段和实体类中属性的映射关系
association 处理多对一的映射关系(处理实体类类型的属性)
column 设置映射关系中的字段名,必须是SQL查询出的某个字段
property 设置映射关系中的属性的属性名,必须是处理的实体类类型中的属性名
collection 处理一对多的映射属性(处理集合类型的属性)
//测试处理字段和属性名不一致情况下的映射关系
Emp getEmpByEmpId(@Param("empId") Integer empId);
<!--Emp getEmpByEmpId(@Param("empId") Integer empId);-->
<resultMap id="empResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
</resultMap>

<select id="getEmpByEmpId" resultMap="empResultMap">
    select * from t_emp where emp_id = #{empId}
</select>

5.2 多对一映射处理

一个部门对应着多个员工,要查询完整的员工信息,就需要根据数据库中员工所在部门的部门id查询到对应的部门信息。

public class Emp {
    private Integer empId;

    private String empName;

    private String age;

    private String gender;

    private Dept dept;
}

//测试类
//连表查询员工和对应的部门的信息
Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);

如何将这个结果封装到Emp实体类中呢

5.2.1 级联查询(自定义映射中处理)

<resultMap id="empAndDeptResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
  	<!--数据库的字段和对象中哪个内部对象的属性映射-->
    <result column="dept_id" property="dept.deptId"/>
    <result column="dept_name" property="dept.deptName"/>
</resultMap>

    <!--Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);-->
<select id="getEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
        select *
        from t_emp
        left join t_dept on t_emp.dept_id = t_dept.dept_id
        where emp_id = #{empId}
</select>

5.2.2 association(处理多对一的映射关系,处理实体类类型的属性)

<resultMap id="empAndDeptResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
    <!--
        association:处理实体类类型的属性
        property:设置需要处理映射关系的属性的属性名
        JavaType:表示要处理属性的类型,使用别名
    -->
    <association property="dept" javaType="Dept">
        <id column="dept_id" property="deptId"/>
        <result column="dept_name" property="deptName"/>
    </association>
</resultMap>

<!--Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);-->
<select id="getEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
    select *
    from t_emp
    left join t_dept on t_emp.dept_id = t_dept.dept_id
    where emp_id = #{empId}
</select>

5.2.3 分步查询

//通过分步查询获取员工和部门的信息第一步
//EmpMapper接口
Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);
//通过分步查询获取员工和部门的信息第二步,查询部门信息
//DeptMapper接口
Dept getEmpAndDeptByStepTwo(@Param("deptId")Integer deptId);
<resultMap id="empAndDeptByStepResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
    <!--
        property:设置需要处理映射关系的属性的属性名
        select:设置分步查询的SQL的唯一标识
        column:将查询出来的某个字段作为分布查询的SQL条件
    -->
    <association property="dept"
                 select="com.zylai.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                 column="dept_id">
    </association>
</resultMap>

<!--Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);-->
<select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
    select * from t_emp where emp_id = #{empId}
</select>

5.3 延迟加载(减少内存消耗)

分布查询的优点:可以实现延迟加载

但是必须在核心文件中设置全局配置信息:

全局设置

lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载

aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性都会按需加载。

此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可以通过association和collection中的fetchType属性

局部(单个)设置

分布查询是否使用延迟加载,fetchType="lazy(延迟加载)|eager(立即加载)"

在核心配置文件中配置

<!--开启延迟加载
    对于分步查询,关联的对象将会延迟加载。
    比如获取emp信息,如果只是打印emp.getEmpName(),用不到Dept的信息
    那么查询Dept的第二步将不会执行。
-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--value为false时按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>

开启之后还可以在mapper映射文件特定sql中的association标签的fetchType属性选择是立即加载还是延迟加载

<resultMap id="empAndDeptByStepResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
    <!--
        fetchType:在开启延迟加载的环境中,指定当前的sql是延迟加载还是立即加载
                   eager表示立即加载,lazy表示懒加载
        property:设置需要处理映射关系的属性的属性名
        select:设置分步查询的SQL的唯一标识
        column:将查询出来的某个字段作为分布查询的SQL条件
    -->
    <association fetchType="eager"
                 property="dept"
                 select="com.zylai.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                 column="dept_id">
    </association>
</resultMap>

<!--Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);-->
<select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
    select * from t_emp where emp_id = #{empId}
</select>

5.4 一对多映射处理

5.4.1 collection

collection标签表示一端中的集合,标签的ofType属性指定集合中的类型

<resultMap id="deptAndEmpResultMap" type="Dept">
    <id column="dept_id" property="deptId"/>
    <result column="dept_name" property="deptName"/>
    <!--ofType指定集合中的类型-->
    <collection property="empList" ofType="Emp">
        <id column="emp_id" property="empId"/>
        <result column="emp_name" property="empName"/>
        <result column="age" property="age"/>
        <result column="gender" property="gender"/>
    </collection>
</resultMap>
<!--Dept getDeptAndEmpByDeptId(@Param("deptId")Integer deptId);-->
<select id="getDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap">
    SELECT * FROM t_dept LEFT JOIN t_emp ON t_dept.dept_id = t_emp.dept_id
    WHERE t_dept.dept_id = #{deptId}
</select>

5.4.2 分步查询

这个已经说过了,同上即可

<!--分步查询-->
<resultMap id="deptAndEmpByStepMap" type="Dept">
    <id column="dept_id" property="deptId"/>
    <result column="dept_name" property="deptName"/>
    <collection property="empList"
                select="com.zylai.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                column="dept_id"/>
</resultMap>

<!--Dept getDeptAndEmpByStepOne(@Param("deptId")Integer deptId);-->
<select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpByStepMap">
    select * from t_dept where dept_id = #{deptId}
</select>
<!--查部门信息,分步的第二步-->
<!--List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);-->
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
    select * from t_emp where dept_id = #{deptId}
</select>

6、动态SQL

使用场景:

Mybatis框架的动态SQL是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼装SQL语句字符串时的痛点问题。

对于客户端传过来的条件,如果没有内容,那么就是null或者空字符串""。

6.1 if标签(条件查询)

通过test属性中的表达式判断标签中的内容是否有效(是否会拼接到SQL中)

//接口
public List<Emp> getEmpByCondition(Emp emp);
<!--
    1、if标签
    需要在where后面紧接着跟上一个恒成立的条件(这里写1=1,也可以直接写true),
    然后在每个拼接的条件前面加上and
-->
<select id="getEmpByConditionOld" resultType="Emp">
    select * from t_emp where 1=1
    <if test="empName != null and empName != ''">
        and emp_name = #{empName}
    </if>
    <if test="age != null and age != ''">
        and age = #{age}
    </if>
    <if test="gender != null and gender != ''">
        and gender = #{gender}
    </if>
</select>

6.2 where标签(条件查询)

1、若where标签中有条件成立,会自动生成一个where关键字进行拼接

2、会自动将where标签中内容前多余的and去掉,但是不会加and,所以在第一个if之后的语句都要加and。也不会将内容后的and去掉

3、如果没有任何一个条件成立,则where没有任何功能,即不会生成where关键字

<select id="getEmpByConditionTwo" resultType="Emp">
    select * from t_emp
    <where>
        <if test="empName != null and empName != ''">
             emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            and age = #{age}
        </if>
        <if test="gender != null and gender != ''">
            and gender = #{gender}
        </if>
    </where>
</select>

6.3 trim标签(条件查询)

prefix,suffix:在整个标签内容前面或后面添加指定的内容
prefixOverrides,suffixOverrides:在标签中内容前面或后面添加指定的内容

<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
    select * from t_emp
    <trim prefix="where" suffixOverrides="and">
        <if test="empName != null and empName != ''">
            emp_name = #{empName} and
        </if>
        <if test="age != null and age != ''">
             age = #{age} and
        </if>
        <if test="gender != null and gender != ''">
             gender = #{gender}
        </if>
    </trim>
</select>

6.4 choose、when、otherwise标签(条件查询)

例如
choose: when(){}
        when(){}
        when(){}
        otherwise(){}
相当于java中的if(){}
             else if(){}
             else if(){}
             else{}
when至少设置一个,otherwise最多设置一个
即只要一个条件满足,后面的条件就不会再判断了
<!--List<Emp> getEmpByChoose(Emp emp);-->
    <!--when中有一个条件满足就不会去拼接了-->
    <select id="getEmpByChoose" resultType="Emp">
        select * from t_emp
        <where>
            <choose>
                <when test="empName != null and empName != ''">
                    emp_name=#{empName}
                </when>
                <when test="age != null and age != ''">
                    age=#{age}
                </when>
                <when test="gender != null and gender != ''">
                    gender=#{gender}
                </when>
            </choose>
        </where>
    </select>

6.5 foreach(批量添加、删除)

本质:循环

  1. collection:设置要循环的数组或集合
  2. item:用一个字符串表示数组或集合中的每一个数据
  3. separator:设置每次循环的数据之间的分隔符
  4. open:循环体之前以什么开始
  5. close:循环体之后以什么结束

遍历集合和数组

<!--foreach标签-->
<!--void insertBatchEmp(List<Emp> empList);-->
<insert id="insertBatchEmp">
    insert into t_emp values
    <foreach collection="empList" item="emp" separator=",">
        (null,#{emp.empName},#{emp.age},#{emp.gender},null)
    </foreach>
</insert>

<!--void deleteBatchEmp(@Param("empIds") Integer[] empIds);-->
<delete id="deleteBatchEmpOne">
    delete from t_emp where emp_id in
    <foreach collection="empIds" item="empId" separator="," open="(" close=")">
        #{empId}
    </foreach>
</delete>

<!--void deleteBatchEmp(@Param("empIds") Integer[] empIds);-->
<delete id="deleteBatchEmp">
    delete from t_emp where
    <foreach collection="empIds" item="empId" separator="or">
        emp_id = #{empId}
    </foreach>
</delete>

6.6 sql标签(sql片段进行记录)

可以记录一段sql,在需要用的地方使用include标签进行引用

<sql id="empColumns">
    emp_id,emp_name,age,gender,dept_id
</sql>

<!--引用语句-->
<include refid="empColumns"/>

<!--使用情况-->
<select id="getEmpByConditionTwo" resultType="Emp">
  select <include refid="empColumns"></include> from t_emp
    <where>
        <if test="empName != null and empName != ''">
             emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            and age = #{age}
        </if>
        <if test="gender != null and gender != ''">
            and gender = #{gender}
        </if>
    </where>
</select>

7、MyBatis的缓存

7.1 MyBatis一级缓存

一级缓存

  • 一级缓存是SqlSession级别的,即通过同一个SqlSession查询的数据会被缓存,再次使用同一个SqlSession查询同一条数据,会从缓存中获取。
  • 一级缓存是默认开启的,一般我们不会去关闭它。

失效的四种情况

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存(sqlSession.clearCache();)
 @Test
    public void testGetEmpById(){
        SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
        CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
        Emp emp1 = mapper1.getEmpById(2);
        System.out.println(emp1);
        //两次查询期间执行了增加操作,任何的增删改都会使缓存失效
        //mapper1.insertEmp(new Emp(null,"小红",18,"男"));

        //两次查询期间,手动清空了缓存
//        sqlSession1.clearCache();
        Emp emp2 = mapper1.getEmpById(2);
        System.out.println(emp2);

        SqlSession sqlSession2 = SqlSessionUtil.getSqlSession();
        CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
        Emp emp3 = mapper2.getEmpById(2);
        System.out.println(emp3);
    }

只查询了一次

7.2 MyBatis二级缓存

二级缓存

  • 二级缓存是SqlSessionFactory级别的,即通过同一个SqlSessionFactory获取的SqlSession对象
  • 查询的数据会被缓存,再通过同一个SqlSessionFactory获取的SqlSession查询相同的数据会从缓存中获取

条件

  • 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
  • 在映射文件中设置标签<cache/>
  • 二级缓存必须在SqlSession关闭或提交之后有效
  • 查询的数据所转换的实体类类型必须实现序列化的接口(implements Serialiazable)
@Test
public void testCache() throws IOException {
    //获取SqlSessionFactory
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    //获取第一个SqlSession
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
    Emp emp1 = mapper1.getEmpById(2);
    System.out.println(emp1);
 		//二级缓存必须在SqlSession关闭或提交之后有效
    sqlSession1.close();

    //获取第一个SqlSession
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
    Emp emp2 = mapper2.getEmpById(2);
    System.out.println(emp2);
    sqlSession2.close();
}

使二级缓存失效的情况:

两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

7.3二级缓存的相关配置

在mapper配置文件中,添加的cache标签可以设置一些属性:

①eviction属性:缓存回收策略,默认的是LRU

属性 功能
LRU(Least Recently Used) 最近最少使用的:移除最长时间不被使用的对象。
FIFO(Fisrt in First out) 先进先出:按对象进入缓存的顺序来移除它们。
SOFT 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK 弱引用:更积极地移除基于垃圾回收器状态和弱引用规则的对象。

②flushInterval属性:刷新间隔,单位毫秒

默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

③size属性:引用数目,正整数

代表缓存最多可以储存多少个对象,太大容易导致内存溢出

④readOnly属性:只读,true/false

true:只读缓存,会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。

false:读写缓存,会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

7.4MyBatis缓存查询的顺序

  1. 查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
  2. 如果二级缓存没有命中,再查询一级缓存
  3. 如果一级缓存也没有命中,则查询数据库
  4. SqlSession关闭之后,一级缓存中的数据会写入二级缓存

7.5整合第三方缓存EHCache

1.添加依赖

        <dependency>
         		<!--Mybatis EHCache整合包-->
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
          	<!--slf4j日志门面的一个具体实现-->
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

2.各种jar包功能

jar包名称 作用
mybatis-ehcache Mybatis和EHCache整合包
ehcache EHCache核心包
slf4j-api SLF4J日志门面包
logback-classic 支持SLF4J门面接口的一个具体实现

3.创建EHCache的配置文件ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <!-- 磁盘保存路径 -->
    <diskStore path="D:\Study\MybatisStudy\ehcache" />
    <defaultCache
            maxElementsInMemory="1"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

        <!-- 属性说明:
         diskStore:当内存中不够存储时,存储到指定数据在磁盘中的存储位置。
         defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略

        以下属性是必须的:
         maxElementsInMemory - 在内存中缓存的element的最大数目
         maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
         eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
         overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上

        以下属性是可选的:
         timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
         timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
         diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
         diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
         memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
        -->

4.设置二级缓存的类型

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

5.加入logback日志

存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志,创建logback的配置文件logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!--指定日志输出位置-->
    <appender>
        <encoder>
        <!--日志输入格式
            按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行
        -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>
    <!--
        设置全局日志级别,日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR
        指定任何一个日志级别都只打印当前级别和后面级别的日志
    -->

    <root level="DEBUG">
        <!--指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender-->
        <appender-ref ref="STDOUT" />
    </root>
    <!--根据特殊需求指定局部日志级别-->
    <logger name="com.atguigu.mybatis.mapper" level="DEBUG" />

</configuration>

8、MyBatis逆向工程

正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。 Hibernate是支持正向工程的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
Java实体类
Mapper接口
Mapper映射文件

8.1 步骤

1 添加依赖和插件

<dependencies>
    <!-- Mybatis核心 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>

    <!-- junit测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
    </dependency>
    <!-- log4j日志 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
</dependencies>

<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
    <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
    <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.0</version>
        <!-- 插件的依赖 -->
        <dependencies>
            <!-- 逆向工程的核心依赖 -->
            <dependency>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-core</artifactId>
                <version>1.3.2</version>
            </dependency>
            <!-- MySQL驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.16</version>
            </dependency>
        </dependencies>
    </plugin>
</plugins>
</build>

2 创建逆向工程的核心文件

文件名必须是:generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--
    targetRuntime: 执行生成的逆向工程的版本
    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
    MyBatis3: 生成带条件的CRUD(奢华尊享版)
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"
                        userId="root"
                        password="root">
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.zylai.mybatis.pojo"
                            targetProject=".\src\main\java">
            <!--是否使用子包-->
            <property name="enableSubPackages" value="true" />
            <!--去掉字段的前后空格-->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.zylai.mybatis.mapper"
                         targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.zylai.mybatis.mapper" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>
    </context>
</generatorConfiguration>

3 使用maven工程的插件执行

image

双击mybatis-generator:generate

之后使用具体的方法即可

注意:生成尊享版本中有根据条件进行插入和更新等。他们是选择性添加或更新,如果指定的字段没有赋值,那么就不会给数据库中的字段赋值为null,而是由数据库的表采用对应的列的默认值

例如:

Emp emp = new Emp(null,"xiaohei",null,女);
//测试普通修改功能
mapper.updateByPrimary(emp);
//该语句会将对应信息,在MySQL字段中对应记录改为NULL

//测试选择性修改
mapper.updateByPrimaryKeySelective(emp);
//该语句在转化为SQL语句时,不会出现修改null所在字段信息

8.2MyBatis3(奢华尊享版)

使用XxxExample类进行条件查询

    @Test
    public void testMBG(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        UserExample userExample = new UserExample();
        //创建条件对象userExample.createCriteria()
      	//.and+根据的属性+条件查询方法()
      	//后面可以再and,比如:userExample.createCriteria().andUsernameEqualTo("admin1").andAgeBetween(1,2)...;
        userExample.createCriteria().andUsernameEqualTo("admin1").andAgeBetween(1,2)
        //mysql语句中,也以and作为条件连接词,对于需要写or的条件
      	userExample.or().andEmailNotBetween(null,null);
        //这样 userExample 转为 sql的条件就是 (X1 and X2) or X3
      	//根据条件查询
        List<User> users = mapper.selectByExample(userExample);
    }

9、分页插件

9.1 分页功能

分页是一个很常用的功能,这就不再赘述,简单的写一下参数

limit index,pageSize
pageSize:每页显示的条数
pageNum:当前页的页码
index:当前页的起始索引,index=(pageNum-1)*pageSize
count:总记录数
totalPage:总页数

方式一:totalPage = (count+pageSize-1)/pageSize

方式二:totalPage = count / pageSize;

满页→添加新页(totalPage++)

if(count % pageSize != 0){
totalPage += 1;
}

pageSize=4,pageNum=1,index=0 limit 0,4
pageSize=4,pageNum=3,index=8 limit 8,4
pageSize=4,pageNum=6,index=20 limit 20,4

pageSize=m,pageNum=n,index=m*(n-1) limit index,pageSize

首页 上一页 2 3 4 5 6 下一页  尾页

9.2 分页插件的使用步骤

1 添加依赖

<!--    分页插件-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</version>
    </dependency>

2 配置分页插件

在MyBatis的核心配置文件中配置

<plugins>
    <!--设置分页插件-->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>

9.3 分页插件的使用

1 在查询之前开启分页功能

相当于一个拦截器,拦截查询的请求以实现分页

在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能

pageNum:当前页的页码
pageSize:每页显示的条数

SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//查询功能之前开启分页功能,相当于一个拦截器,拦截查询的请求实现分页
Page<Object> page = PageHelper.startPage(5, 4);
//实际会执行两条sql,第一条查询总记录数。第二条才是分页查询
List<Emp> list = mapper.selectByExample(null);
//查询功能之后可以获取分页相关的所有数据
PageInfo<Emp> pageInfo = new PageInfo<>(list,5);
System.out.println(pageInfo);
list.forEach(System.out::println);

//page继承了ArrayList,本质上是一个集合。里面有许多我们用到的信息
System.out.println(page);

PageInfo{
pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,
list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,
pages=8, reasonable=false, pageSizeZero=false},
prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true,
hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8,
navigatepageNums=[4, 5, 6, 7, 8]
}

2 查询获取list集合

在查询获取list集合之后,使用PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages)获取分页相关数据

list:分页之后的数据
navigatePages:导航分页的页码数

查询出来的数据示例:

* PageInfo{
* pageNum=2, pageSize=4, size=4,
* startRow=5, endRow=8,total=30, pages=8,
* list=Page{count=true, pageNum=2, pageSize=4,
* startRow=4, endRow=8, total=30, pages=8, reasonable=false,pageSizeZero=false}
* [Emp{empId=5, empName='aa4', age=14, gender='女', deptId=null},
* Emp{empId=6, empName='aa5', age=15, gender='女', deptId=null},
* Emp{empId=7, empName='aa6', age=16, gender='女', deptId=null},
* Emp{empId=8, empName='aa7', age=17, gender='女', deptId=null}],
* prePage=1, nextPage=3, isFirstPage=false, isLastPage=false,
* hasPreviousPage=true, hasNextPage=true, navigatePages=5,
* navigateFirstPage=1, navigateLastPage=5,
* navigatepageNums=[1, 2, 3, 4, 5]
* }

3 分页的相关数据

pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]

标签:mapper,缓存,查询,sqlSession,emp,MyBatis,id
From: https://www.cnblogs.com/mlstudyjava/p/16748635.html

相关文章

  • Mybatis使用注解方式插入数据后 返回自增长的主键值
    需求:插入数据时不插入主键,让主键自增,但是需要知道插入的数据主键是啥。注意:insert方法的返回值,表示成功添加的条数,和主键ID无关实现:添加注解@Options()示例@Insert(......
  • mybatis-spring的pom.xml
    <?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"......
  • Springboot整合Redis作为Mybatis的二级缓存
    参考原文:https://juejin.cn/post/6971740313501368356一级缓存是:sqlSession,sql建立连接到关闭连接的数据缓存二级缓存是:全局的缓存准备配置启动类添加注解:@EnableC......
  • 【mybatis框架学习】三、invoke方法逻辑编排
    上一篇一直有提高一个词,编排。都说编程,编程,编排也就容易理解了。 像我们常用的框架,spring、mybatis,都是将一些固有的流程,简化,抽象,编排起来,在留有可拓展的接口之后,全部......
  • 【mybatis框架学习】一、序言
    相关内容需要基本的mybatis框架使用经验,不会过多赘述基本常识性的内容。关于mybatis的使用,概括来讲就是:配置jdbc连接信息、配置sql语句、定义mapper接口、定......
  • mybatis puls学习笔记(二)
    mapperpackagecom.ychen.mybatis.mapper;importcom.baomidou.mybatisplus.core.conditions.Wrapper;importcom.baomidou.mybatisplus.core.mapper.BaseMapper;importcom......
  • Mybatis plus代码生成器
    案例一demo为​​chenx/mybatisplus-demo​​​​参考​​​​案例​​项目初始结构数据库新建表项目配置启动CodeGenerator类中的main方式,输入表名,生成代码案例二demo为​......
  • Mybatis plus案例
    前言当表名为user时,会多生成2个实体类正常情况下生成的类测试是否可以直接在当前​​mybatis代码生成器的项目​​中开发启动项目后测试,发现当前项目只能用来生成代码即使项......
  • mybatis plus 项目模板
    前言​​案例地址​​项目搭建新建1个springboot项目,导入所需依赖点击查看详情<dependencies><dependency><groupId>org.springframework.boot</groupId>......
  • MyBatisPlus查询对象转QueryWrapper工具类
    技术背景在使用MyBatisPlus技术实际项目中,尤其是后台管理系统之类的项目中,经常会出现大量的需要将查询对象转换成QueryWrapper的应用场景,这时候就需要编写出现大量的转换代......