Mybatis笔记

1.Mybatis简介

1.1MyBatis特性

MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架

MyBatsi避免了几乎所有的JDBC代码和手动设置参数以及获取结果集

MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

MyBatis是一个半自动的ORM(Object Relation Mapping)框架

1.2持久层技术对比

1.2.1JDBC

SQL夹杂在Java代码中耦合度高,导致硬编码内伤

维护不易且实际开发需求中SQL有变化,频繁修改的情况多见

代码冗长,开发效率低

1.2.2Hibernate

操作简单,开发效率高

程序中的长难复杂SQL需要绕过框架

内部自动生产的SQL,不容易做特殊优化

基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难

反射操作太多,导致数据库性能下降

1.2.3MyBatis

轻量级,性能出色

SQL和Java编码分开,功能边界清晰,Java代码专注业务、SQL语句专注数据

开发效率稍逊于Hibernate,但是完全能够接受

2.MyBatis框架搭建及简单测试

ORM:Object Relationship Mapping,对象关系映射。

  • 对象:Java的实体类的对象
  • 关系:关系型数据库
  • 映射:二者之间的对应关系
Java概念数据库概念
属性字段/列
对象2.1MyBatis配置文件记录/行

2.1MyBatis配置文件

resources下新建mybatis-config.xml文件,写入配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--配置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/XXX"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <mapper resource="mapper/XXXMapper.xml"
    </mappers>
</configuration>

2.2MyBatis映射文件创建

2.2.1映射文件的命名规则

表所对应的实体类的类名+Mapper.xml,如:UserMapper.xml

因此一个映射文件对应一个实体类,对应一张表的操作

MyBatis映射文件用于编写SQL,访问以及操作表中的数据

MyBatis映射文件存放的位置是src/main/resources/mappers目录下

2.2.2MyBatis面向接口编程的一致性

【表----实体类----mapper接口----映射文件】

MyBatis面向接口编程的两个一致:

  1. 映射文件的namespace要和mapper接口的全类名保持一致
  2. 映射文件中SQL语句的id要和mapper接口中的方法名一致
Mapper.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.....mapper.XXXMapper">
Mapper接口中:
public interface UserMapper{
	//添加用户信息
	int insertUser();
}


Mapper.xml中:
<insert id="insertUser">
	insert into t_user values(null,'admin','123456',23,'男','123456@qq.com')
</insert>

2.3MyBatis简单测试-添加功能

通过sqlSession.getMapper(XXXMapepr.class)方法来获取mapper接口

调用mapper接口执行完SQL后需要手动提交事务

@Test
public void test() throws IOException{
	//加载核心配置文件
	InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
	//获取SqlSessionFactoryBuilder
	SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
	//获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
    //获取SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //获取mapper接口对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //测试功能
    int result = mapper.insertUser();
    //手动提交事务,不提交事务的话没效果
    sqlSession.commit();
    sout("result: "+result);
}

2.4功能优化

2.4.1自动提交事务

上述实现,在每一次SQL执行后都需要手动提交事务,sqlSession默认不自动提交事务

若需要自动提交事务,可以使用sqlSessionFactory.openSession(true)方法,把参数设置成true,默认是false,true表示自动提交

@Test
public void test() throws IOException{
	//加载核心配置文件
	InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
	//获取SqlSessionFactoryBuilder
	SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
	//获取SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
    //获取SqlSession,参数设置为true为自动提交,默认是false
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    //获取mapper接口对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //测试功能
    int result = mapper.insertUser();
    sout("result: "+result);
}

2.4.2加入log4j日志功能

在SQL执行过程中希望在控制台打印出执行过程,可加入log4j日志功能

1.加入依赖:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2.加入log4j的配置文件:

log4j的配置文件名为log4j.xml,存放的位置是src/main/resources目录下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <!-- 将日志信息输出到控制台 -->
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encodeing" value="UTF-8" />
        <!-- 设置日志输出的样式 -->
        <layout class="org.apache.log4j.PatternLayout">
            <!-- 设置日志输出的格式 -->
            <param name="ConversionPattern" value="%-5p %d{yyyy-MM-dd HH:mm:ss:SSS} %m(%F:%L) \n" />
        </layout>
        <!--过滤器设置输出的级别-->
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <!-- 设置日志输出的最小级别 -->
            <param name="levelMin" value="WARN" />
            <!-- 设置日志输出的最大级别 -->
            <param name="levelMax" value="ERROR" />
            <!-- 设置日志输出的xxx,默认是false -->
            <param name="AcceptOnMatch" value="true" />
        </filter>
    </appender>
    
    <logger name="java.sql">
        <level value="debug" />
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info" />
    </logger>
    <root>
        <level value="debug" />
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

日志的级别:

FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)

从左到右打印的内容越来越详细

2.5MyBatis其他功能测试

2.5.1修改功能

从接口开始写,先在mapper接口里面定义方法,再写.xml中的实现SQL

void updateUser();
<update id="updateUser">
	update t_user set username = "张三" where id = 4
</update>
@Test
public void testUpdate() throws IOException{
	InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.updateUser();//无返回值
}

2.5.2删除功能

void delUser();
<update id="delUser">
	delete from t_user where id = 4
</update>
@Test
public void testDel() throws IOException{
	InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.delUser();//无返回值
}

2.5.3查询功能

查询是有返回值的,在写之前要设置好

注意:

  • 查询功能的标签必须设置resultType或resultMap,不然无法找到返回值类型进行映射
  • resultType:设置默认的映射关系
  • resultMap:设置自定义的映射关系
2.5.3.1查询单个信息
//根据id返回查询用户信息
User getUserById();
<select id="getUserById" resultType="com.....pojo.User">
	select * from t_user where id = 3
</select>
@Test
public void test() throws IOException{
	InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //测试,返回一个User对象
    User user = mapper.getUserById;
    sout(user);
}
2.5.3.2查询集合信息
//查询所有的用户信息
List<User> getAllUser();
<select id="getAllUser" resultType="com....pojo.User" >
	select * from t_user
</select>
@Test
public void test() throws IOException{
	InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //测试,返回一个User对象
    List<User> userList = mapper.getAllUser;
    userList.forEach(user -> sout(user));
}

3.核心配置文件

3.1environment详解

  • environments:配置多个连接数据库的环境

    属性:
    default:设置默认使用的环境id

  • environment:配置某个连接数据库的具体环境
    属性:
    idL:表示连接数据库环境的唯一标识,不能重复

  • transactionManager:设置事务管理方式

    属性:

    ​ type=”JDBC|MANAGED“

    ​ JDBC:表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,事务的提交或回滚需要手动处理

    ​ MANAGED:被管理,例如Spring

  • dataSource:配置数据源

    属性:

    ​ type:设置数据源的类型

    ​ type=”POOLED|UNPOOLED|JNDI“

    ​ POOLED:表示使用数据库连接池缓存数据库连接

    ​ UNPOOLED:表示不使用数据库连接池

    ​ JNDI:表示使用上下文中的数据源

<!--配置多个连接数据库的环境-->
<environments default="development">
	<!--配置某个连接数据库的具体环境-->
    <environment id="development">
    	<!--设置事务管理方式-->
        <transactionManager type="JDBC"></transactionManager>
        <!--配置数据源-->
        <dataSource type="POOLED">
        	<!--设置连接数据库的驱动-->
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <!--设置连接数据库的地址-->
            <property name="url" value="jdbc:mysql://localhost:3306/XXX"/>
            <!--设置连接数据库的用户名-->
            <property name="username" value="root"/>
            <!--设置连接数据库的密码-->
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
    
    <environment id="test">
        <transactionManager type="JDBC"></transactionManager>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/XXX"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>

3.2properties详解

可以创建jdbc.properties文件来单独存放数据库配置信息

jdbc.properties文件中:

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

mybatis-config.xml文件中:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

	<!--引入properties文件,绑定properties文件中的配置-->
	<rpoperties resource="jdbc.properties" />
	
    <!--配置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
            	<!--value值可以替换成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>
    
    <!--引入映射文件-->
    <mappers>
        <mapper resource="mapper/XXXMapper.xml"
    </mappers>
    
</configuration>

3.3typeAliases详解

如下,在resultType里面需要每一次都写实体类路径全称,比较麻烦

<select id="getAllUser" resultType="com....pojo.User" >
	select * from t_user
</select>

MyBatis提供了类型别名的功能:typeAliases标签,设置默认/指定的类型别名。写了alias属性代表指定用"User"直接代替"com…pojo.user",如果不写则默认使用实体类名代替,且不区分大小写

  • typeAliases:设置类型别名

  • typeAlias:设置某个类型的别名

    属性:

    ​ type:设置需要设置别名的类型

    ​ alias:设置某个类型的别名。如设置,则是指定值;如不设置,则默认使用实体类的类名,不区分大小写

  • package:以包为单位,将包下所有的类型设置默认的类型别名,即类名且不区分大小写

注意:MyBatis核心配置文件中,标签需要按照顺序去配置,顺序如下:

properties?----settings?----typeAliases?----typeHandlers?----objectFactory?----objectWrapperFactory?----reflectorFactory?----plugins?----environments?----databaseIdProvider?----mappers?

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

	<!--引入properties文件,绑定properties文件中的配置-->
	<properties resource="jdbc.properties" />
	
	<!--设置类型别名-->
	<typeAliases>
		<!--写了alias属性代表指定用"User"直接代替"com....pojo.user",如果不写则默认使用实体类名代替,且不区分大小写-->
    	<!--<typeAlias type="com....pojo.user" alias="User"><typeAlias/>-->
    	<!--以包为单位,将包下所有的类型设置默认的类型别名,即类名且不区分大小写-->
    	<package name="com....pojo"/>
    </typeAliases>
	
    <!--配置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
            	<!--value值可以替换成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>
    
    <!--引入映射文件-->
    <mappers>
        <!--name里面填入.xml文件放置的包名-->
		<package name="com.atguigu.mybatis.mapper" />
    </mappers>
    
</configuration>

上述类型别名设置后,.xml中的resultType值可以直接使用实体类名,且不区分大小写

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

3.4mappers详解

上述映射文件的引入,都是单个引入,后续.xml文件增多后不太方便

<!--引入映射文件-->
<mappers>
	<mapper resource="mapper/XXXMapper.xml"
</mappers>

MyBatis提供以包为单位进行映射文件的引入

  • package:以包为单位引入映射文件

    要求:不然无法绑定识别

    1. mapper接口所在的包要和映射文件所在的包一致
    2. mapper接口要和映射文件的名字一致
<mappers>
	<!--name里面填入.xml文件放置的包名-->
	<package name="com.atguigu.mybatis.mapper"
</mappers>

注意:resources底下创建包时没有package,只能创建directory,创建的时候名字不能直接用“com.atguigu.mybatis.mapper”,这种创建出来的只是一个文件夹,没有目录结构,需要使用“com/atguigu/mybatis/mapper”去创建,这样才是一个有目录层级的包,创建完成后也会转换成“.”的格式

4.IDEA中使用MyBatis

4.1IDEA中模板设置

打开File–Settings–Editor–File and Code Template

然后添加mybatis-config模板,将内容复制到这里面,模板名Name为mybatis-config,后缀Extension为xml

这样的话在新项目中配置mybatis就可以直接创建模板文件,只需要在里面修改内容即可

其他模板创建也类似,如创建mybatis-mapper.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="">
    
</mapper>

在这里插入图片描述

4.2封装SqlSessionUtils工具类

获取sqlSession对象的过程是重复的,我们可以把它单独拧出来封装在utils工具类中

创建一个utils包,新建SqlSessionUtils类,在里面写获取SqlSession对象的代码

public class SqlSessionUtils {
	
	public static SqlSession getSqlSession() {
		SqlSession sqlSession = null;
		try {
			//加载核心配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //获取SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            //获取SqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
		} catch(IOException e) {
			e.printStackTrace();
		}
		return sqlSession;
	}	
}

测试使用:

@Test
public void test(){
	//直接调用SqlSessionUtils工具类获取sqlSession对象
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getAllUser;
    userList.forEach(user -> sout(user));
}

5.MyBatis获取参数值情况

MyBatis获取参数值的两种方式:${}和#{}

  • ${}:本质字符串拼接,要注意单引号‘ ’问题
  • #{}:本质占位符赋值,更推荐使用
  • 以上两种可根据实际场景选用

MyBatis获取参数值的各种情况:

5.1mapper接口方法的参数为单个

可以通过KaTeX parse error: Expected 'EOF', got '#' at position 4: {}和#̲{}以任意的名称获取参数值,但…{}的单引号问题。具体使用如下:

mapper接口中:

//根据用户名查询用户
User getUserByUsername(String username);

mapper.xml中:

注意:#{username}的值不管填什么都不影响占位符赋值,例如:#{aaa}也可以获取参数值。但是一般推荐使用属性名/字段名,提高代码可阅读性

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

使用 :需要加单引号’‘,不然会报错,使用形 式 ′ {}:需要加单引号’ ‘,不然会报错,使用形式' :需要加单引号’‘,不然会报错,使用形username}',里面的值跟#{}一样,不受名称影响

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

测试:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserByUsername(admin);
    sout(user);
}

5.2mapper接口方法的参数为多个

此时,MyBatis会将这些参数放在一个map集合中,以两种方式进行存储

  1. 以arg0,arg1,…为键,以参数为值
  2. 以param0,param1,…为键,以参数为值
  3. 以上两种,可以混用,如:arg0,param1方式也可以正常访问

因此,只需要通过#{}和 以键的方式访问值即可,但是需要注意 {}以键的方式访问值即可,但是需要注意 以键的方式访问值即可,但是需要注意{}的单引号问题。具体使用如下:

mapper接口中:

//验证登录
User checkLogin(String username,String password);

mapper.xml中:

<select id="checkLogin" resultType="User">
	<!--不能再使用 #{username}方式获取,只能使用MyBatis定义好的arg0/param0方式-->
	select * from t_user where username = #{arg0} and password = #{arg1}
</select>
<select id="checkLogin" resultType="User">
	<!--不能再使用 '${username}'式获取,只能使用MyBatis定义好的arg0/param0方式-->
	select * from t_user where username = '${param0}' and password = '${param1}'
</select>

测试:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.checkLogin("admin","123456");
    sout(user);
}

5.3手动设置map存储多参数

若mapper接口方法的参数有多个时,可以手动将这些参数放在一个map中存储。可以通过KaTeX parse error: Expected 'EOF', got '#' at position 4: {}和#̲{}以任意的名称获取参数值,但…{}的单引号问题。具体使用如下:

mapper接口中:

//验证登录(参数为map)
User checkLoginByMap(Map<String,Object> map);

mapper.xml中:

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

测试:在map中设置访问的键,则SQL中直接用map的键即可访问

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map = new HashMap<>();
    map.put("username",admin");
    map.put("password","123456");
    User user = mapper.checkLoginByMap(map);
    sout(user);
}

5.4mapper接口方法的参数是实体类

实际情况中,可能从前端传值一个对象进行操作,这时mapper接口方法参数需要是实体类类型

只需要通过KaTeX parse error: Expected 'EOF', got '#' at position 4: {}和#̲{}以属性的方式来访问属性值即…{}的单引号问题。具体使用如下:

mapper接口中:

//添加用户信息
int insertUser(User user);

mapper.xml中:

<insert id="insertUser">
	insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email})
</insert>

测试:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int result = mapper.insertUser(null,"李四","123","21","男","123@qq.com");
    sout(result);
}

5.5使用@Param注解命名多参数

可以使用@Param注解来自定义多参数中Map集合访问的键

此时MyBatis会将这些参数放在一个map集合中,以两种方式进行存储

  1. 以@Param注解的值为键,以参数为值
  2. 以param0,param1,…为键,以参数为值

因此只需要通过KaTeX parse error: Expected 'EOF', got '#' at position 4: {}和#̲{}以键的方式访问值即可,但是…{}的单引号问题。具体使用如下:

mapper接口中:

//验证登录(使用@param)
User checkLoginByParam(@Param("username") String username, @Param("password") String password)

mapper.xml中:

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

测试:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.checkLoginByMap("admin","123456");
    sout(user);
}

以上五种情况,除了传输对象(可直接通过属性名访问)外,如果传输的是单个/多个参数值时,建议都使用@Param注解来标识键,比较便捷

6.MyBatis增删改查

6.1MyBatis各种查询

MyBatis的各种查询功能:

  1. 若查询出的数据只有一条

    1. 可以通过实体类对象接收
    2. 可以通过List集合接收【List集合返回值可以为一条数据/null,不会报错】
    3. 可以通过Map结合接收:以字段为键,字段的值为值
  2. 若查询出的数据有多条

    1. 可以通过实体类类型的List集合接收

    2. 可以通过Map类型的List集合接收

    3. 可以在mapper接口的方法上添加@MapKey(“”)注解,此时就可以将每条数据转换的map集合作为值,以某个字段的值作为键,放在同一个map集合中

      注意:一定不能通过实体类对象接收,此时会抛异常TooManyResultsException

6.1.1查询一个/多个实体类对象

mapper中:

//根据id查询用户信息
User getUserById(@Param("id") Integer id);

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

mapper.xml中

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

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

测试类

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserById(3);
    sout(user);
}

@Test
public void testGetAllUser(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getAllUser;
    sout(userList);
}

6.1.2查询单行/单列的值

MyBatis中设置了默认的类型别名,且不区分大小写。常用如下:

  1. java.lang.Integer–>int, integer
  2. int–>_int, _integer
  3. Map–>map
  4. String–>string

mapper中:

//查询用户信息的总记录数
Integer getCount();

mapper.xml中:这里可以直接写Integer/integer/int/Int都行

<select id="getCount" resultType="Integer">
	select count(*) from t_user
</select>

测试类

@Test
public void getCount(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int count = mapper.getCount;
    sout(count);
}

6.1.3查询返回一个/多个Map集合

使用场景:如果我们查询出来的结果没有任何一个实体类可以与之相对应(如多表查询),则可以用Map集合来接收,Map中就是以字段为键,以字段的值为值

注意:

  • 查询多条数据时,每条数据会存放在一个map中,此时有多个map,不能只用一个map接收,要用List<map<>>来接收,不然会跟单个实体类一样的抛异常TooManyResultsException
  • 如果不想用List来接收多个map,可以使用@MapKey(“”)注解来指定记录的键,然后记录作为值保存,这样就可以用单个map来接收了,指定的键必须要有唯一性,一般使用id来作为这个键

mapper中:

//根据id查询用户信息为map集合
Map<String,Object> getUserByIdToMap;

//查询所有用户信息为map集合
List<Map<String,Object>> getAllUserToMap;

//查询所有用户信息为map集合
@MapKey("id")
Map<String,Object> getAllUserToMapByMapKey;

mapper.xml中

<select id="getUserByIdToMap" resultType="map">
	select * from t_user where id = #{id}
</select>

<select id="getAllUserToMap" resultType="map">
	select * from t_user
</select>

<select id="getAllUserToMapByMapKey" resultType="map">
	select * from t_user
</select>

测试类

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map = mapper.getUserByIdToMap(3);
    sout(map);
}

@Test
public void testGetAllUser(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<Map<String,Object>> mapList = mapper.getAllUser;
    sout(mapList);
}

@Test
public void testGetAllUser(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map = mapper.getAllUserToMapByMapKey;
    sout(map);
}

6.2特殊SQL执行

6.2.1模糊查询

mapper中:

//根据用户名模糊查询用户信息
List<User> getUserByLike(@Param("username") String username);

mapper.xml中:

  • 注意:直接使用’%#{}%'时,#{}不会解析成占位符,会被?代替,被当成字符串的一部分,此时没有占位符,但是我们却要为占位符赋值,便会直接报错,可以使用以下三种方式解决:
<!--1.使用${}解决-->
<select id="getUserByLike" resultType="User">
	select * from t_user where username like '%${username}%'
</select>

<!--2.使用concat进行字符串拼接-->
<select id="getUserByLike" resultType="User">
	select * from t_user where username like concat('%',#{username},'%')
</select>

<!--3.直接使用双引号""来拼接,推荐使用-->
<select id="getUserByTableName">
	select * from ${tableName}
</select>

测试类

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserByLike("a");
    sout(userList);
}

6.2.2批量删除

mapper中:

//批量删除
int deleteMore(@Param("ids") String ids);

mapper.xml中

  • 注意:在SQL执行过程中,#{}会自动加单引号’ ',但是使用where id in ()时不能加单引号,SQL语句格式不正确,所以只能使用${}来获取参数,而不能用#{}来获取参数
<delete id="deleteMore">
	delete from t_user where id in (${ids})
</delete>

测试类

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
   	int result = mapper.deleteMore("1,2,3");
    sout(result);
}

6.2.3动态设置表名

mapper中:

//查询指定表中的数据
List<User> getUserByTableName(@Param("tableName") String tableName);

mapper.xml中:

  • 注意:在SQL执行过程中,表名是不能加单引号的,所以也不能用#{}来传参,只能用${}
<select id="getUserByTableName">
	select * from ${tableName}
</select>

测试类:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserByTableName("t_user");
    sout(userList);
}

6.2.4添加功能获取自增的主键

mapper中:

//添加用户信息
void insertUser();

mapper.xml

  • useGeneratedKeys:设置当前标签中的sql使用了自增的主键
  • keyProperty:将自增的主键的值赋值给传输搭配映射文件中参数的某个属性
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
	insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email})
</insert>

测试类:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  	User user = new User(null,"张三","123",23,"男","123.@qq.com")
  	mapper.insertUser(user);
  	sout(user);
}

7.MyBatis映射关系

7.1自定义映射resultMap

当数据库字段名和实体类属性值不一致时,例如:数据库中字段使用下划线连接user_name,而实体类属性需要使用驼峰命名userName,此时查询时用userName会显示null,没有匹配的字段,无法映射,需要自定义映射

解决字段名和属性名不一致的情况:

  1. 为字段起别名,保持和属性名一致
  2. 设置全局配置,将_自动映射为驼峰:使用时需保证数据库字段符合下划线命名方式,属性也符合驼峰命名。
  3. 通过resultMap解决字段和属性名的映射关系

7.1.1通过设置字段别名解决字段名和属性名的映射关系

在xml中的SQL语句中指定字段别名user_name userName来完成映射

<select id="getAllUser" resultType="User">
	select id,user_name userName,age,sex,email from t_user
</select>

7.1.2通过全局配置mapUnderscoreToCamelCase解决字段名和属性名的映射关系

在mybatis-config.xml中添加settings进行全局配置,来统一将数据库下划线_字段转换为驼峰形式

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<!--设置MyBatis的全局配置-->
	<settings>
		<!--将下划线_自动映射为驼峰,user_name:userName-->
		<setting name="mapUnderscoreToCamelCase" value=true >
	</settings>
</configuration>

7.1.3通过resultMap解决字段和属性名的映射关系

使用resultMap标签可以自定义设置字段与属性间的映射关系

  • id:唯一标识,不能重复,在SQL语句中的resultMap的值便是设置的id的值

  • type:设置映射关系中的实体类类型

  • 子标签:

    • id:设置主键的映射关系
    • result:设置普通字段的映射关系
    • 属性:
      1. property:设置映射关系中的属性名,必须是type属性所设置的实体类类型中的属性名,对应实体类
      2. column:设置映射关系中的字段名,必须是SQL语句查询出的字段名,对应数据库

mapper.xml中:

<resultMap id="UserResultMap" type="User">
    <id property="id" column="id" />
    <result property="userName" column="user_name" />
    <result property="age" column="age" />
    <result property="sex" column="sex" />
    <result property="email" column="u_email" />
</resultMap>

<!--返回值类型不用resultType,改为resultMap,值为上述resultMap设置的id值-->
<select id="getAllUser" resultMap="UserResultMap">
	select * from t_user
</select>

7.2映射关系:多对一

多对一:对应对象,返回一个对象

处理多对一的映射关系:

  1. 级联属性赋值
  2. association标签处理
  3. 分布查询,使用较多的方式

7.2.1通过级联属性赋值解决多对一的映射关系

mapper中:

//查询员工以及员工所对应的部门信息
Emp getEmpAndDept(@Param("eid") Integer eid);

mapper.xml中:

<resultMap id="empAndDeptResultMap" type="Emp">
    <id property="eid" column="eid" />
    <result property="empName" column="emp_name" />
    <result property="age" column="age" />
    <result property="sex" column="sex" />
    <result property="email" column="u_email" />
    <!--处理多对一映射关系方式一:级联属性赋值-->
    <result property="dept.did" column="did" />
    <result property="dept.deptName" column="dept_name" />
</resultMap>

<select id="getEmpAndDept" resultMap="empAndDeptResultMap">
	select * from t_emp left join t_dept on t_emp.did = t_dept.did 
	where t_emp.eid = #{eid}
</select>

测试类:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  	Emp emp = mapper.getEmpAndDept(1);
  	sout(emp);
}

7.2.2通过association解决多对一的映射关系

association:专门处理多对一的映射关系

  • property:需要处理的属性名
  • javaType:该属性的类型

mapper.xml中:

<resultMap id="empAndDeptResultMap" type="Emp">
    <id property="eid" column="eid" />
    <result property="empName" column="emp_name" />
    <result property="age" column="age" />
    <result property="sex" column="sex" />
    <result property="email" column="u_email" />
    <!--处理多对一映射关系方式二:使用association标签来专门处理,javaType填的是另一个实体类的类型-->
    <association property="dept" javaType="Dept">
    	<id property="did" column="did">
    	<result property="deptName" column="dept_name">
    </association>
</resultMap>

<select id="getEmpAndDept" resultMap="empAndDeptResultMap">
	select * from t_emp left join t_dept on t_emp.did = t_dept.did 
	where t_emp.eid = #{eid}
</select>

7.2.3通过分布查询解决多对一的映射关系

mapper中:

//EmpMapper中:
//通过分布查询查询员工以及员工所对应的部门信息
//分布查询第一步:查询员工信息
Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);

//DeptMapper中:
//通过分布查询查询员工以及员工所对应的部门信息
//分布查询第二步,通过did查询员工所对应的部门
Dept getEmpAndDeptByStepTwo(@Param("did") Integer did)

mapper.xml中:

使用association属性引用分步查询结果

  • select:设置分步查询的SQL的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
  • column:设置分布查询的条件
<resultMap id="empAndDeptByStepResultMap" type="Emp">
    <id property="eid" column="eid" />
    <result property="empName" column="emp_name" />
    <result property="age" column="age" />
    <result property="sex" column="sex" />
    <result property="email" column="u_email" />
    <!--处理多对一映射关系方式三:使用association标签的属性引用分部查询结果-->
    <association property="dept" 
    			 select="com.....DeptMapper.getEmpAndDeptByStepTwo"
    			 column="did">
    </association>
</resultMap>

<select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
	select * from t_emp where eid = #{eid}
</select>

<!--DeptMapper.xml中:-->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
	select * from t_dept where did = #{did}
</select>

测试类:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  	Emp emp = mapper.getEmpAndDeptByStepOne(1);
  	sout(emp);
}

7.3延迟加载

分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息

lazyLoadingEnable:表示延迟加载是否可用,延迟加载的全局开关。当开启时,所有关联对象都会延迟加载

aggressiveLazyLoading:表示是否按需加载。当开启时,任何方法的调用都会加载该对象的所有属性,不会延迟加载,且设成true后lazyLoadingEnable效果丢失,所以要默认false。否则,每个属性会按需加载

此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”

如果要实现延迟加载:查询员工就只查员工,查询部门就只查部门,不会关联查询员工及对应部门关系。需要将lazyLoadingEnable开启,aggressiveLazyLoading关闭,然后使用fetchType属性来实现按需加载

如下:在mybatis-config.xml中设置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<!--设置MyBatis的全局配置-->
	<settings>
		<!--将下划线_自动映射为驼峰,user_name:userName-->
		<setting name="mapUnderscoreToCamelCase" value="true" >
		<!--开启延迟加载,开启后只会执行当前方法,不会关联查询其他数据,如果不需要延迟加载,则在association中添加fetchType属性来设置-->
		<setting name="lazyLoadingEnable" value="true" >
	</settings>
</configuration>

mapper.xml中:

使用association属性引用分步查询结果

  • select:设置分步查询的SQL的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
  • column:设置分布查询的条件
  • fetchType:当开启了全局的延迟加载之后,可通过此属性手动控制延迟加载的效果
    • fetchType=“lazy”:表示延迟加载
    • fetchType=“eager”:表示立即加载
    • 注意:如果不开启全局延迟加载,则fetchType不管填什么值都是立即加载的效果
<resultMap id="empAndDeptByStepResultMap" type="Emp">
    <id property="eid" column="eid" />
    <result property="empName" column="emp_name" />
    <result property="age" column="age" />
    <result property="sex" column="sex" />
    <result property="email" column="u_email" />
    <!--处理多对一映射关系方式三:使用association标签的属性引用分部查询结果-->
    <association property="dept" 
    			 select="com.....DeptMapper.getEmpAndDeptByStepTwo"
    			 column="did"
    			 fetchType="eager">
    </association>
</resultMap>

<select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
	select * from t_emp where eid = #{eid}
</select>

<!--DeptMapper.xml中:-->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
	select * from t_dept where did = #{did}
</select>

7.4映射关系:一对多

一对多:对应集合,返回一个集合

处理一对多的映射关系:

  1. collection标签处理
  2. 分步查询

7.4.1通过collection解决一对多的映射关系

Demp实体类中:

//用一个集合来表示所有的员工信息
private List<Emp> emps;

get()方法

set()方法

重写toString()方法

mapper中:

//获取部门以及部门中所有的员工信息
Dept getDeptAndEmp(@Param("did") Integer did);       

mapper.xml中:

collection:处理一对多的映射关系

  • ofType:表示该属性所对应的集合中存储数据的类型
<resultMap id="deptAndEmpResultMap" type="Dept">
    <id property="did" column="did" />
    <result property="deptName" column="dept_name" />
    <collection property="emps" ofType="Emp">
    	<id property="eid" column="eid" />
        <result property="empName" column="emp_name" />
        <result property="age" column="age" />
        <result property="sex" column="sex" />
        <result property="email" column="u_email" />
   	<collection/>
</resultMap>


<select id="getDeptAndEmp" resultMap="deptAndEmpResultMap">
	select * from t_dept left join t_emp on t_dept.did = t_emp.did
	where t_dept.did = #{did}
</select>

测试类:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  	Dept dept = mapper.getDeptAndEmp(1);
  	sout(dept);
}

7.4.2通过分步查询解决一对多的映射关系

mapper中:

//DeptMapper中:
//通过分步查询查询部门以及部门中所有的员工信息
//分步查询第一步:查询部门信息
Dept getDeptAndEmpByStepOne(@Param("did") Integer did);

//EmpMapper中:
//通过分步查询查询部门以及部门中所有的员工信息
//分步查询第二步:根据did查询员工信息
List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did)

mapper.xml中:

<resultMap id="deptAndEmpByStepResultMap" type="Dept">
    <id property="did" column="did" />
    <result property="deptName" column="dept_name" />
    <collection property="emps"
    			select="com.....EmpMapper.getDeptAndEmpByStepTwo"
    			column="did">
    <collection/>
</resultMap>

<select id="getDeptAndEmpByStepOne" resultType="deptAndEmpByStepResultMap">
	select * from t_dept where did = #{did}
</select>

<!--EmpMapper.xml中:-->
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
	select * from t_emp where did = #{did}
</select>

测试类:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  	Dept dept = mapper.getDeptAndEmp(1);
  	sout(dept);
}

8.动态SQL

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

8.1if标签

if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行。

if标签:

  • 根据标签中test属性所对应的表达式决定标签中的内容是否需要拼接到SQL中

mapper中:

//多条件查询
List<Emp> getEmpByCondition(Emp emp);

mapper.xml中:

where后面加上"1=1"的恒成立条件,避免出现如果第一条if判断不执行时,后面的语句直接执行where and报错

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

测试类:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  	Emp emp = mapper.getEmpByCondition(null,"张三",23,"男","123@qq.com");
  	sout(dept);
}

8.2where标签

上述if标签中的where关键字是写死了,避免报错需要加上一个1=1恒成立条件,但是Mybatis提供where标签来动态生成where关键字,还会自动去掉多余的and/or,不用额外加条件

where标签:

  • 当where标签中有内容时,会自动生成where关键字,并且将内容前多余的and或or去掉
    • 注意:where标签不能将其中内容后面多余的and或or去掉,使用时最好在前面加and或or
  • 当where标签中没有内容时,此时where标签没有任何效果

mapper.xml中:

<select id="getEmpByCondition" 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="sex != null and sex !=''">
            and sex = #{sex}
        </if>
        <if test="email != null and email !=''">
            and email = #{email}
        </if>
 	</where>
</select>

8.3trim标签

上述使用where标签可以去掉内容前面多余的and或or,但是无法去掉内容后面多余的and或or,下面可以用trim标签来实现在内容前面或后面添加/去掉指定内容

trim标签:

  • 若标签中有内容时:
    • prefix|suffix:将trim标签中内容前面或后面添加指定内容
    • prefixOverrides|suffixOverrides:将trim标签中内容内容前面或后面去掉指定内容
  • 若标签中没有内容时:trim标签也没有任何效果

mapper.xml中

<select id="getEmpByCondition" resultType="Emp">
	select * from t_emp 
	<trim prefix="where" suffixOverrides="and|or">
        <if test="empName != null and empName !=''">
            emp_name = #{empName}
        </if>
        <if test="age != null and age !=''">
            and age = #{age}
        </if>
        <if test="sex != null and sex !=''">
            and sex = #{sex}
        </if>
        <if test="email != null and email !=''">
            and email = #{email}
        </if>
 	</trim>
</select>

8.4choose、when、otherwise标签

choose、when、otherwise是一套标签,相当于if…else if…else

  • choose相当于父标签,表示if…else if…else组合

  • when相当于else if,至少要有一个

  • otherwise相当于else,最多只能有一个

mapper中:

//测试choose、when、otherwise
List<Emp> getEmpByChoose(Emp emp);

mapper.xml中:

注意:只会执行满足条件的一条when,不用加and或or

<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="sex != null and sex !=''">
                sex = #{sex}
            </when>
            <when test="email != null and email !=''">
                email = #{email}
            </when>
            <otherwise>
            	did = 1
            </otherwise>
		</choose>
 	</where>
</select>

测试类:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  	List<Emp> empList = mapper.getEmpByChoose(null,"张三",23,"男","123@qq.com");
  	sout(empList);
}

8.5foreach标签

用来做循环操作,如批量删除或批量添加

foreeach标签:

  • collection:设置需要循环的数组或集合
  • item:表示数组或集合中的每一个数据
  • separator:循环体之间的分隔符
  • open:foreach标签所循环的所有内容的开始符
  • close:foreach标签所循环的所有内容的结束符

mapper中:

//通过数组实现批量删除
int deleteMoreByArray(@Param("eids") Integer[] eids);
//通过List集合实现批量添加
int insertMoreByList(@Param("emps") List<Emp> emps);

mapper.xml中:

<!--批量删除第一种写法where id in (...),分隔符用","-->
<delete id="deleteMoreByArray">
	delete from t_emp where eid in
	(
		<foreach collection="eids" item="eid" separator=",">
			#{eid}
		</foreach>
	)
</delete>

<!--将左右括号放到foreach标签中用open和close来注明,表示循环体外加括号-->
<delete id="deleteMoreByArray">
	delete from t_emp where eid in
    <foreach collection="eids" item="eid" separator="," open="(" close=")">
    	#{eid}
    </foreach>
</delete>

<!--批量删除第二种写法where id=? or id=?,分隔符用"or"-->
<delete id="deleteMoreByArray">
	delete from t_emp where
    <foreach collection="eids" item="eid" separator="or">
			eid = #{eid}
   	</foreach>
</delete>
<insert id="insertMoreByList">
	insert into t_emp values
	<foreach collection="emps" item="emp" separator=",">
		(null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null)
	</foreach>
</insert>

测试类:

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
   	int result = mapper.deleteMoreByArray(new Integer[]{6,7,8});
    sout(result);
}
@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Emp emp1 = new Emp(null,"a1",23,"男","123@qq.com");
    Emp emp2 = new Emp(null,"a2",23,"男","123@qq.com");
    Emp emp3 = new Emp(null,"a3",23,"男","123@qq.com");
    List<Emp> emps = Arrays.cList(emp1,emp2,emp3);
   	int result = mapper.insertMoreByList(emps);
    sout(result);
}

8.6sql标签

将常用的sql片段拿出来进行记录,当需要用的时候直接引用

sql标签:

  • 设置sql片段:eid,emp_name,age,sex,email
  • 引用sql片段:

mapper.xml中:

<sql id="empColums">
	eid,emp_name,age,sex,email
</sql>

<!--用include标签引入sql片段-->
<select id="getEmpByCondition" resultType="Emp">
	select <include refid="empColums"></include> from t_emp 
	<trim prefix="where" suffixOverrides="and|or">
        <if test="empName != null and empName !=''">
            emp_name = #{empName}
        </if>
        <if test="age != null and age !=''">
            and age = #{age}
        </if>
        <if test="sex != null and sex !=''">
            and sex = #{sex}
        </if>
        <if test="email != null and email !=''">
            and email = #{email}
        </if>
 	</trim>
</select>

9.MyBatis缓存

将当前查询出的数据进行记录,等下一次再查询相同数据的时候,直接从缓存中取,不用重新访问数据库

MyBatis中的缓存分为一级缓存和二级缓存,一级缓存是默认开启的

9.1一级缓存SqlSession

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问

如:使用同一个SqlSession连续查询相同的数据,则只会执行一条SQL,其余的数据从缓存中拿取返回

@Test
public void test(){
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
   	Emp emp1 = mapper.getEmpById(1);
   	sout(emp1);
   	Emp emp2 = mapper.getEmpById(1);
    sout(emp2);
}

//执行效果:一条SQL语句,两条打印结果(emp1,emp2),后面那条是直接从缓存拿的,不用重新执行一次SQL

使一级缓存失效的四种情况:

  1. 不同的SqlSession对应不同的一级缓存

  2. 同一个SqlSession但是查询条件不同

  3. 同一个SqlSession两次查询期间执行了任何一次增删改操作

  4. 同一个SqlSession两次查询期间手动清空了缓存:使用sqlSession.clearCache()方法

    @Test
    public void test(){
    	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
       	Emp emp1 = mapper.getEmpById(1);
       	sout(emp1);
       	
       	//清空缓存
       	sqlSession.clearCache()
       	
       	Emp emp2 = mapper.getEmpById(1);
        sout(emp2);
    }
    
    //执行效果:一条SQL,一条打印数据;再一条SQL,再一条打印数据
    

9.2二级缓存SqlSessionFactory

二级缓存的范围比一级缓存的范围要大一些,且需要手动设置

9.2.1二级缓存概念

二级缓存是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取

二级缓存开启的条件:4个条件缺一不可

  1. 在核心配置文件钟工,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
  2. 在映射文件中设置标签,直接放在mapper.xml文件中即可
  3. 二级缓存必须在SqlSession关闭或提交之后有效
  4. 查询的数据所转换的实体类类型必须实现序列化的接口,implements Serializable

使二级缓存失效的情况:

  • 唯一情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
  • 注意:sqlSession.clearCache()手动清空缓存只对一级缓存有效,无法使二级缓存失效

9.2.2二级缓存功能用法

二级缓存的用法测试如下:

全局配置中:默认cacheEnabled=“true”,不需要特别设置

实体类中:需要实现序列化接口

public class Emp implements Serializable{}

mapper.xml中:直接使用标签来开启二级缓存

<cache />

<select>
	select * .......
</select>

需要测试二级缓存,此时就不要使用封装SqlSessionUtils工具类了,在测试方法中重新实现,一个sqlSessionFactory对应创建多个sqlSession来进行测试

注意:每个sqlSession执行完后必须使用sqlSession.close()来关闭才会有效,否则缓存命中率Cache Hit Ratio为0,二级缓存无效;加上sqlSession.close()后,第二个sqlSession的缓存命中率Cache Hit Ratio为0.5,不需要重新执行SQL,从二级缓存中拿取数据。

@Test
public void testCache() throws IOException{
	try{
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
        
        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
       	CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
       	sout(mapper1.getEmpById(1));
       	sqlSession1.close();//需要关闭或提交后二级缓存才会有效,否则缓存命中率Cache Hit Ratio为0
       	
       	SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
       	CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
       	sout(mapper1.getEmpById(1));
       	sqlSession2.close();
 	} catch(IOException e){
 		e.printStackTrace();
 	}
}

9.2.3二级缓存相关配置

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

  • eviction属性:缓存回收策略

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

    默认的是LRU

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

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

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

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

  • readOnly属性:只读,true/false

    • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改,这提供了很重要的性能优势
    • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false

9.3缓存查询顺序

MyBatis缓存查询的顺序如下:

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

9.4整合第三方缓存EHCache

MyBatis是持久层框架,做缓存不太专业,提供了第三方接口来做二级缓存,但是一级缓存还是原生

9.4.1依赖导入

引入依赖:

<!--Mybatis EHCache整合包-->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>

<!--slf4j日志门面的一个具体实现-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

各jar包功能:

  • mybatis-ehcache:MyBatis和EHCache的整合包
  • ehcache:EHCache核心包
  • slf4j-api:SLF4J日志门面包
  • logback-classic:支持SLF4J门面接口的一个具体实现

9.4.2EHCache配置

创建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:\atguigu\ehcache" />

    <defaultCache
            maxElementsInMemory="10000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

在mapper.xml文件中使用设置type类型为EHCache缓存,不设置则默认使用MyBatis原生的二级缓存

<cache type="org.mybatis.caches.ehcache.EHCacheCache">

9.4.3加入logback日志

存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志

创建logback的配置文件logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<!-- 日志输出的格式 -->
			<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、执行 -->
			<pattern>[%d{HH:mm:ss.SSS}] [%-Sleve] [%thread] [$logger] [%msg]%n</pattern>
		</encoder>
	</appender>

	<!--设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR-->
	<!--指定任何一个日志级别都只打印当前级别和后面级别的日志-->
    <root level="DEBUG">
    	<!--指定答应日志的appender,这里通过"STDOUt"引用了前面配置的appender-->
        <appender-ref ref="STUOUT" />
    </root>
    
    <!--根据特殊需求指定局部日志级别-->
    <logger name="com.atguigu.crowd.mapper" level="DEBUG">
    
</configuration>

到此,第三方缓存就配置完成了,像以前使用MyBatis原生的缓存一样使用即可,示例详见原二级缓存的使用

10.MyBatis逆向工程

10.1正向/逆向工程

  • 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的

  • 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:

    • Java实体类
    • Mapper接口
    • Mapper映射文件

逆向工程主要用来自动生成代码

10.2逆向工程的配置&使用

添加依赖和插件:

<!--依赖MyBatis核心包-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>

<!--控制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>
                
                <!--数据库连接池-->
                <dependency>
                    <groupId>com.mchange</groupId>
                    <artifactId>c3p0</artifactId>
                    <version>0.9.2</version>
                </dependency>
                
                <!--MySQL驱动-->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.1.8</version>
                </dependency>
          	</dependencies>
     	</plugin>
	</plugins>
</build>

创建配置文件:文件名必须是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">
    	<!-- 数据库的连接信息 -->
        <!-- 数据库连接驱动类,URL,用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/mybatis"
                        userId="root" 
                        password="123456">
        </jdbcConnection>
        
        <!-- javaBean的生成策略 -->
       	<!-- 生成Domain模型:包名(targetPackage)、位置(targetProject) -->
        <javaModelGenerator targetPackage="com.atguigu.mybatis.domain" targetProject="\src\main\java">
            <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
            <property name="enableSubPackages" value="true"/>
            <!-- 设置是否在getter方法中,对String类型字段调用trim()方法-->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        
        <!-- SQL映射文件的生成策略 -->
        <!-- 生成xml映射文件:包名(targetPackage)、位置(targetProject) -->
        <sqlMapGenerator targetPackage="com.atguigu.mybatis.mapper" targetProject="\src\main\resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
 
        <!-- Mapper接口的生成策略 -->
        <!-- 生成DAO接口:包名(targetPackage)、位置(targetProject) -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.mapper" targetProject="\src\main\java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
 
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName-->
        <!-- domainObjectName属性指定生成出来的实体类的类名-->
        <!-- 要生成的表:tableName - 数据库中的表名或视图名,domainObjectName - 实体类名 -->
        <table tableName="t_emp" domainObjectName="Emp" />
        <table tableName="t_dept" domainObjectName="EmpDept />
 
    </context>
</generatorConfiguration>

以上配置完成后,在IDEA右上角的maven工程里面找到mybatis-generator:generate插件,然后直接双击运行,成功后即可生成文件

10.3奢华尊享版代码使用

在上述generatorConfig.xml配置中,我们可以生成两种代码,其中MyBatis3Simple生成后只有基础CRUD代码,MyBatis3生成后会在domain里面额外增加EmpExample、DeptExample类,用来实现条件查询

MyBatis3Simple:生成基本的CRUD(清新简洁版)
MyBatis3:生成带条件的CRUD(奢华尊享版)

奢华尊享版查询功能测试:

@Test
public void test() throws IOException{
	try{
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
       	EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
       	//查询所有数据,无条件查询,传入值为null
       	List<Emp> list = mapper.selectByExample(null);
       	list.foreach(emp->sout(emp));
       	
       	//根据条件查询,首先创建一个EmpExample
       	EmpExample example = new EmpExample();
       	//使用createCriteria()创造条件,后面带上字段条件,且可以通过.链式结构一直加条件,下面表示查询姓名为“张三”且年龄大于等于20的信息
       	example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThanOrEqualTo(20);
       	//如果想用or条件,直接用or(),后面继续跟andXXX条件,即表示与上一个条件是or关系
       	example.or().andDidIsNotNull();
       	
       	//将example放入查询中,即实现带条件查询
       	List<Emp> list = mapper.selectByExample(example);
       	list.foreach(emp->sout(emp));
 	} catch(IOException e){
 		e.printStackTrace();
 	}
}

奢华尊享版修改功能测试:

@Test
public void test() throws IOException{
	try{
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
       	EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
       	//根据主键进行修改
       	mapper.uodateByPrimaryKey(1,"admin",22,null,"456@qq,com");//null也会直接修改null
       	mapper.uodateByPrimaryKeySelective(1,"admin",22,null,"456@qq,com");//选择性修改,null不会修改,直接忽略
 	} catch(IOException e){
 		e.printStackTrace();
 	}
}

11.MyBatis分页插件

11.1分页插件使用步骤

导入依赖:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

配置分页插件:

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

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

11.2分页插件的使用

数据库分页查询关键字:limit index,pageSize

MyBatis分页插件使用:page(pageNum,pageSize)

  • index:当前页的起始索引
  • pageSize:每页显示的条数
  • pageNum:当前页的页码
  • index=(pageNum-1)*pageSize

使用MyBatis的分页插件实现分页功能:

  1. 需要在查询功能之前开启分页:

    PageHelper.startPage(int pageNum,int pageSize);

  2. 在查询分页之后获取分页相关信息:

    PageInfo page = new PageInfo<>(list,5);

    • list:表示分页数据
    • 5:表示当前导航分页的数据,显示5个导航分页,建议奇数,左边展示2个,右边展示2个

常用数据:

  • pageNum:当前页的页码
  • pageSize:每页显示的条数
  • size:当前页显示的条数
  • total:总记录数
  • pages:页数
  • prePage:上一页的页码
  • nextPage:下一页的页码
  • isFirstPage/isLastPage:是否为第一页/最后一页
  • hasPreviousPage/hasNextPage:是否存在上一页/下一页
  • navigatePage:导航分页的页码数
  • navigatepageNums:导航分页的页码,[1,2,3,4,5]
@Test
public void test() throws IOException{
	try{
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
       	EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
       	//查询之前开启分页插件,访问第1页,显示4条数据,返回值是一个page对象
       	PageHelper.startPage(1,4);
       	List<Emp> list = mapper.selectByPage(null);
       	PageInfo<Emp> page = new PageInfo<>(list,5);
       	list.foreach(emp->sout(emp));      	
 	} catch(IOException e){
 		e.printStackTrace();
 	}
}
public void test() throws IOException{
	try{
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
       	EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
       	//根据主键进行修改
       	mapper.uodateByPrimaryKey(1,"admin",22,null,"456@qq,com");//null也会直接修改null
       	mapper.uodateByPrimaryKeySelective(1,"admin",22,null,"456@qq,com");//选择性修改,null不会修改,直接忽略
 	} catch(IOException e){
 		e.printStackTrace();
 	}
}

11.MyBatis分页插件

11.1分页插件使用步骤

导入依赖:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

配置分页插件:

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

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

11.2分页插件的使用

数据库分页查询关键字:limit index,pageSize

MyBatis分页插件使用:page(pageNum,pageSize)

  • index:当前页的起始索引
  • pageSize:每页显示的条数
  • pageNum:当前页的页码
  • index=(pageNum-1)*pageSize

使用MyBatis的分页插件实现分页功能:

  1. 需要在查询功能之前开启分页:

    PageHelper.startPage(int pageNum,int pageSize);

  2. 在查询分页之后获取分页相关信息:

    PageInfo page = new PageInfo<>(list,5);

    • list:表示分页数据
    • 5:表示当前导航分页的数据,显示5个导航分页,建议奇数,左边展示2个,右边展示2个

常用数据:

  • pageNum:当前页的页码
  • pageSize:每页显示的条数
  • size:当前页显示的条数
  • total:总记录数
  • pages:页数
  • prePage:上一页的页码
  • nextPage:下一页的页码
  • isFirstPage/isLastPage:是否为第一页/最后一页
  • hasPreviousPage/hasNextPage:是否存在上一页/下一页
  • navigatePage:导航分页的页码数
  • navigatepageNums:导航分页的页码,[1,2,3,4,5]
@Test
public void test() throws IOException{
	try{
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
       	EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
       	//查询之前开启分页插件,访问第1页,显示4条数据,返回值是一个page对象
       	PageHelper.startPage(1,4);
       	List<Emp> list = mapper.selectByPage(null);
       	PageInfo<Emp> page = new PageInfo<>(list,5);
       	list.foreach(emp->sout(emp));      	
 	} catch(IOException e){
 		e.printStackTrace();
 	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值