MyBatis

Mybatis

MyBatis介绍

1、MyBatis的介绍

MyBatis最初是Apache的一个开源项目iBatis,2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下,iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到了Github。

iBatis一词来源于Internet和abatis的组合,是一个基于Java的持久层框架。iBatis提供的持久层框架包括SQL Maps和Data Access Object(DAO)。

Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

总之,Mybatis对JDBC访问数据库的过程进行了封装,简化了JDBC代码,解决JDBC将结果集封装为Java对象的麻烦。

下图是MyBatis架构图:

(1)mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂

(2)基于SqlSessionFactory可以生成SqlSession对象

(3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。

(4)Executor是SqlSession底层的对象,用于执行SQL语句

(5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)

2、MyBatis特性

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

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

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

  • MyBatis是一个半自动的ORM(ObjectRelation Mapping)框架

3、MyBatis下载

MyBatis下载地址 https://github.com/mybatis/mybatis-3

4、MyBatis和其他持久层技术的对比

JDBC

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

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

  • 代码冗长、开发效率低

Hibernate和JPA

  • 操作简便,开发效率高

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

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

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

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

MyBatis

  • 轻量级,性能出色

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

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

MyBatis入门

1、准备数据

2、创建Maven的java工程

3、添加依赖

在pom.xml文件中引入相关依赖包即可

<dependencies>

<!-- junit单元测试 -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

</dependency>

<!-- mysql驱动 -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.32</version>

</dependency>

<!-- mybatis -->

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>3.5.11</version>

</dependency>

</dependencies>

4、创建MyBatis的核心配置文件

核心配置文件主要用于配置连接数据库的环境以及Mybatis的全局配置信息,将来SSM整合后,这个配置文件可以省略。

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE configuration

PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

"" target="_blank">https://mybatis.org/dtd/mybatis-3-config.dtd" target="_blank">">

<configuration>

<environments default="development">

<environment id="development">

<transactionManager type="JDBC" />

<dataSource type="POOLED">

<property name="driver" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://localhost:3306/shenmu?characterEncoding=utf-8" />

<property name="username" value="root" />

<property name="password" value="root" />

</dataSource>

</environment>

</environments>

<mappers>

<!-- 引入mapper文件 -->

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

</mappers>

</configuration>

了解映射关系:

  • 数据库中一张表对应Java中的一个class(tb_useràUser),一条表记录对应一个对象

  • 一张表对应一个Mapper文件,如对tb_user表所做的CRUD操作,都写在UserMapper中

  • 一个Mapper文件对应一个Dao层接口的,一条sql就对应接口中的一个方法

使用Mybatis需要关注:

  • SqlSession对象:是mybatis提供给我们用来和数据库进行交互的对象

  • mapper文件:写sql语句的,一般一张表对应一个mapper文件

  • mapper接口:一个接口对应一个mapper文件,接口中的一个方法就对应mapper文件中的一条sql

5、创建实体类

/**

* 此实体类User和表tb_user表对应

* 到时候tb_user中的一条表记录就可以用一个User对象封装

* 因此此类中属性和表中字段一一对应

*/

public class User {

private Integer id;//用户ID

private String username;//用户名

private String password;//用户密码

private Double account;//账户余额

//无参,全参,get+set,toString

6、创建mapper接口

public interface UserDao {

//新增员工数据

intinsertUser();

}

一个接口对应一个mapper文件,接口中的一个方法对应mapper文件中的一条sql

7、创建mapper映射文件(记得要在核心配置文件中配置)

相关概念ORM(Object RelationShipMapping) 对象关系映射。

对象:Java的实体类对象

关系: 关系型数据库

映射: 二者之间的对应关系

Java中的类 -> 数据库中的表

类的属性 -> 表中字段(列)

对象 -> 表记录(行)

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"" target="_blank">http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--

MyBatis支持面向接口编程的,我们的一个接口就对应这里的一个mapper文件

接口中的方法就对应mapper文件中的一条sql,他们是如何对应起来的?

public interface UserDao {

//新增员工数据

int insertUser();

}

如上,mapper文件namespace的值=接口的全限定类名

mapper文件中的一条sql语句的id属性值=接口中的一个方法名

-->

<mapper namespace="cn.niit.dao.UserDao">

<insert id="insertUser">

insert into tb_user values(null,'赵六','123',5000)

</insert>

</mapper>

8、测试

public class TestMyBatis {

@Test

public void testMyBatis() throws IOException {

//1、加载核心配置文件

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

//2、创建SqlSessionFactoryBuilder对象

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

//3、创建SqlSessionFactory

SqlSessionFactory factory = builder.build(in);

//4、创建SqlSession

SqlSession sqlSession = factory.openSession();

//5、获取UserDao接口的实现类

UserDao userDao = sqlSession.getMapper(UserDao.class);

//6、调用对应的方法,执行方法对应的sql,获取结果

int rows = userDao.insertUser();

System.out.println("受影响的行数:"+rows);

}

}

SqlSession:代表Java程序和数据库之间的会话(HttpSession是Java程序和浏览器间的会话)

sqlSessionFactory:是生产SqlSession的工厂

工厂模式:如果创建某一个对象,使用的过程基本固定,那么就可以把创建这个对象相关的代码封装到一个工厂类中,以后就可以都使用这个工厂类来生产我们需要的这个对象了。

测试:发现控制台有打印但是数据没有新增成功

这是因为我们配置的事务是JDBC,则需要手动提交事务

修改代码:

public class TestMyBatis {

@Test

public void testMyBatis() throws IOException {

//1、加载核心配置文件

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

//2、创建SqlSessionFactoryBuilder对象

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

//3、创建SqlSessionFactory

SqlSessionFactory factory = builder.build(in);

//4、创建SqlSession

SqlSession sqlSession = factory.openSession();

//5、获取UserDao接口的实现类

UserDao userDao = sqlSession.getMapper(UserDao.class);

//6、调用对应的方法,执行方法对应的sql,获取结果

int rows = userDao.insertUser();

//提交事务

sqlSession.commit();

System.out.println("受影响的行数:"+rows);

}

}

9、设置自动提交事务

SqlSession默认不自动提交事务,可以设置自动提交事务

public class TestMyBatis {

@Test

public voidtestMyBatis() throws IOException {

//1、加载核心配置文件

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

//2、创建SqlSessionFactoryBuilder对象

SqlSessionFactoryBuilderbuilder = new SqlSessionFactoryBuilder();

//3、创建SqlSessionFactory

SqlSessionFactory factory = builder.build(in);

//4、创建SqlSession

SqlSession sqlSession = factory.openSession(true);

//5、获取UserDao接口的实现类

UserDao userDao = sqlSession.getMapper(UserDao.class);

//6、调用对应的方法,执行方法对应的sql,获取结果

int rows = userDao.insertUser();

//提交事务

//sqlSession.commit();

System.out.println("受影响的行数:"+rows);

}

}

10、加入log4j日志功能

导入log4J的jar包

<!-- 整合log4j -->

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>1.6.4</version>

</dependency>

在项目中加入log4j的配置文件,用于打印日志信息,便于开发调试。

在src(或相似的目录)下创建log4j.properties如下:

# Globallogging configuration

log4j.rootLogger=DEBUG, stdout

# Consoleoutput...

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

mybatis默认使用log4j作为输出日志信息。

只要将该文件放在指定的位置,log4j工具会自动到指定位置加载上述文件,读取文件中的配置信息并使用!

日志级别:

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

日志级别越低,打印的内容越详细

使用MyBatis实现数据的CRUD

配置好MyBatis的环境后,对于同一张表的操作,只需要在接口中添加方法,到mapper文件中写对应的sql,通过sqlSession获取接口的实现类对象并调用对应的方法即可。

修改id为1的用户余额为2000

添加方法

//修改id为1的用户余额为2000

int updateUserById();

写sql语句

<!-- 修改id为1的用户余额为2000

int updateUserById(); -->

<update id="updateUserById">

update tb_user set account = 2000 where id = 1

</update>

测试

@Test

public void testMyBatis() throws IOException {

//1、加载核心配置文件

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

//2、创建SqlSessionFactoryBuilder对象

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

//3、创建SqlSessionFactory

SqlSessionFactory factory = builder.build(in);

//4、创建SqlSession

SqlSession sqlSession = factory.openSession(true);

//5、获取UserDao接口的实现类

UserDao userDao = sqlSession.getMapper(UserDao.class);

//6、调用对应的方法,执行方法对应的sql,获取结果

//int rows = userDao.insertUser();

int rows = userDao.updateUserById();

//提交事务

//sqlSession.commit();

System.out.println("受影响的行数:"+rows);

}

练习:删除id为3的用户信息

查询id为1的用户信息

添加方法

//查询id为1的员工数据

User seleteUserById();

写sql

<!-- //查询id为1的用户数据User seleteUserById();

查询功能的标签必须设置resultType或resultMap

resultType: 设置默认的映射关系(字段和属性名一致,用resultType自动映射即可)

resultMap:设置自定义的映射关系

-->

<select id="seleteUserById" resultType="cn.niit.entity.User">

select * from tb_user where id = 1;

</select>

测试

@Test

public void testMyBatis() throws IOException {

//1、加载核心配置文件

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

//2、创建SqlSessionFactoryBuilder对象

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

//3、创建SqlSessionFactory

SqlSessionFactory factory = builder.build(in);

//4、创建SqlSession

SqlSession sqlSession = factory.openSession(true);

//5、获取UserDao接口的实现类

UserDao userDao = sqlSession.getMapper(UserDao.class);

//6、调用对应的方法,执行方法对应的sql,获取结果

//int rows = userDao.insertUser();

//int rows = userDao.updateUser();

//int rows = userDao.deleteUser();

User user = userDao.seleteUserById();

System.out.println(user);

//提交事务

//sqlSession.commit();

//System.out.println("受影响的行数:"+rows);

}

查询所有用户数据

添加方法

//查询所有用户数据

List<User> selectAllUser();

写sql

<!-- //查询所有用户数据 List<User> selectAllUser(); -->

<select id="selectAllUser" resultType="cn.niit.entity.User">

select * from tb_user

</select>

测试

@Test

public void testMyBatis() throws IOException {

//1、加载核心配置文件

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

//2、创建SqlSessionFactoryBuilder对象

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

//3、创建SqlSessionFactory

SqlSessionFactory factory = builder.build(in);

//4、创建SqlSession

SqlSession sqlSession = factory.openSession(true);

//5、获取UserDao接口的实现类

UserDao userDao = sqlSession.getMapper(UserDao.class);

//6、调用对应的方法,执行方法对应的sql,获取结果

List<User> list = userDao.selectAllUser();

for (User user : list) {

System.out.println(user);

}

}

MyBatis核心配置文件

1、配置文件详解

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE configuration

PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

"" target="_blank">https://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<!-- 一个environments中可以配置多个environment

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

一个environment对应一个数据库环境

id属性值为当前环境的唯一标识,不能重复

-->

<environments default="development">

<environment id="development">

<!-- transactionManager:设置事务管理方式

type="JDBC|MANAGED"

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

MANAGED:被管理,比如spring

-->

<transactionManager type="JDBC" />

<dataSource type="POOLED">

<!-- dataSource:配置数据源

type:设置数据源的类型

type="POOLED|UNPOOLED|JNDI"

POOLED:表示使用连接池

UNPOOLED:表示不使用连接池

JNDI:表示使用上下文中的数据源(已过时)

-->

<property name="driver" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://localhost:3306/shenmu?characterEncoding=utf-8" />

<property name="username" value="root" />

<property name="password" value="root" />

</dataSource>

</environment>

</environments>

<!-- 一个mappers中可以配置多个mapper -->

<mappers>

<!-- 引入mapper文件 -->

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

</mappers>

</configuration>

2、jdbc.properties

准备jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://localhost:3306/shenmu?characterEncoding=utf-8

jdbc.username=root

jdbc.password=root

在核心配置文件中引入properties文件

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE configuration

PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

"" target="_blank">https://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<properties resource="jdbc.properties"/>

<environments default="development">

<environment id="development">

<transactionManager type="JDBC" />

<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>

<mappers>

<!-- 引入mapper文件 -->

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

</mappers>

</configuration>

3、设置别名

<!-- 设置别名 -->

<typeAliases>

<!-- typeAlias用来设置某个类型的别名(别名不区分大小写)

type:需要设置别名的类型

alias:设置的别名,若不设置则默认为类名且不区分大小写

-->

<!-- <typeAlias type="cn.niit.entity.User"/> -->

<!-- 以包为单位,为包下所有类型设置默认的别名即类名且不区分大小写 -->

<package name="cn.niit.entity"/>

</typeAliases>

测试

<!-- //查询所有用户数据 List<User> selectAllUser(); -->

<select id="selectAllUser" resultType="user">

select * from tb_user

</select>

MyBatis获取参数值的两种方式

准备一个工具类,封装重复代码

public class SqlSessionUtil {

public staticSqlSession getSqlSession(String path) {

SqlSession sqlSession = null;

try {

//读取mybatis核心配置文件

InputStream in = Resources.getResourceAsStream(path);

//构建SqlSessionFactory工厂

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

//创建SqlSession对象

sqlSession = factory.openSession(true);

} catch (IOException e) {

e.printStackTrace();

}

return sqlSession;

}

}

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

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

  • ${} 使用字符串拼接的方式拼接sql,若参数值为字符串或日期类型,则需要手动加单引号

  • #{} 使用占位符赋值的方式拼接sql,此时参数值为字符串或日期类型,会自动添加单引号

  • 能用#{}就不用${},有时会有需要用${}的情况

  • 需要注意的是${}取值,必须将值放到map中,且一般用的是#{},而${}多用于为不带引号的字符串进行占位。

1、单个字面量类型的参数

若mapper接口中的方法参数为单个的字面量类型

此时可以使用${}和#{}以任意名称获取参数的值,注意${}需要手动加单引号。

也可以为参数加上@Param注解,则mybatis会将此参数放到map集合中,以@Param注解的值、param1,param2...为key,值为参数值。这样我们需要通过key去获取对应的参数值了。

练习:查询指定id的用户信息

添加方法

public interface UserDao {

//查询指定id的用户信息

User selectUserById(@Param("id") Integer id);

}

写sql

<!-- 查询指定id的用户信息

User selectUserById(@Param("id") Integer id); -->

<select id="selectUserById" resultType="user">

select * from tb_user where id = #{id}

</select>

测试:

@Test

public voidtestSelectById() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

User user = userDao.selectUserById(1);

System.out.println(user);

}

2、多个字面量类型的参数

若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中,默认以arg0,arg1…为key,以参数为值value。以param1、param2…为为key,以参数为值value;因此只需要通过${}和#{}访问map集合中的key就可以获取对应的value值。注意${}需要手动添加单引号。

也可以为每个参数加上@Param注解,则mybatis会将这些参数放到一个map集合中,以两种方式存储

  • 以@Param的值为key,参数值为值

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

因此只需要通过#{}/${}通过key取参数值即可,注意${}需要手动添加单引号

练习:验证用户的用户名和密码

准备接口中的方法

public interface UserDao {

//查询指定id的用户信息

User selectUserById(@Param("id") Integer id);

//验证用户的用户名和密码

User selectUserByUserAndPasswd(@Param("username") String username,@Param("passwd")String passwd);

}

写sql

<!--

验证用户的用户名和密码

User selectUserByUserAndPasswd(String username,String passwd);

当传多个值时,mybatis会自动将这些值都放到map中,

key为arg0,arg1..或者param1,param2...

User selectUserByUserAndPasswd(@Param("username") String username,@Param("passwd")String passwd);

或者可以选择加上@Param注解来指定map中的key

-->

<select id="selectUserByUserAndPasswd" resultType="user">

<!-- select * from tb_user where username = #{arg0} and password = #{param2} -->

select * from tb_user where username = #{username} and password = #{passwd}

</select>

测试

@Test

public void testSelectUserByUsernameAndPasswd() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

User user = userDao.selectUserByUserAndPasswd("张三", "123");

System.out.println(user);

}

3、多个参数用Map封装传值

当参数有多个时,可以手动将这些参数放到一个map中存储,只需要通过#{}和${}以key的方式获取对应的参数值即可,注意${}需要手动添加单引号。

练习:新增用户

接口中添加方法

public interface UserDao {

//查询指定id的用户信息

User selectUserById(@Param("id") Integer id);

//验证用户的用户名和密码

User selectUserByUserAndPasswd(@Param("username") String username,

@Param("passwd")String passwd);

//新增用户

int insertUser(Map<String, Object> map);

}

写sql

<!-- 新增用户

int insertUser(Map<String, Object> map);

Map<String, Object> map = new HashMap<>();

map.put("username", "张飞");

map.put("passwd", "zf");

map.put("account", 1000);

int rows = userDao.insertUser(map);

-->

<insert id="insertUser">

insert into tb_user values(null,#{username},#{passwd},#{account})

</insert>

测试

@Test

public voidtestInsertUser() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

Map<String, Object> map = new HashMap<>();

map.put("username", "张飞");

map.put("passwd", "zf");

map.put("account", 1000);

int rows = userDao.insertUser(map);

System.out.println("受影响的行数:"+rows);

}

4、多个参数用实体类对象封装传值

修改用户信息:

当多个参数使用实体类对象封装传值,只需要使用#{}或者${}以属性的方式访问对应的属性值即可,注意${}需要手动添加单引号

接口中添加方法

//修改用户信息(用实体类传参)

int updateUserById(User user);

写sql

<!-- //修改用户信息(用实体类传参)

int updateUserById(User user);

当参数值封装在java对象中传过来时,#{}以属性的方式访问对应的属性值 -->

<update id="updateUserById">

update tb_user set username=#{username},password=#{password},account=#{account} where id = #{id}

</update>

测试

@Test

public voidtestUpdateUser() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

User user = new User(6, "孙策", "sc", 10000.0);

userDao.updateUserById(user);

}

MyBatis的各种查询

1、查询结果为单行单列的数据

若查询结果是单行单列的数据,则按对应类型接收即可,可以用别名

MyBatis中设置了默认的类型别名:

java.lang.Integer à int,integer(别名不区分大小写)

int -> _int,_integer

Map -> map

String -> string

方法:

//查询总记录数

Integer selectCount();

sql:

<!-- 查询总记录数 Integer selectCount(); -->

<select id="selectCount" resultType="int">

select count(*) from tb_user;

</select>

测试:

@Test

public voidtestSelectCount() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

Integer count = userDao.selectCount();

System.out.println("总记录数为:"+count);

}

2、查询结果为一条表记录

若查询结果只有一条数据

  • 可以用实体类对象接收

  • 可以用map接收

并且他们可以放到list集合中

结果用实体类接收(略)

结果可以用map集合接收

方法

//查询指定id的用户信息

Map<String, Object> selectUserById(@Param("id") Integer id);

sql:

<select id="selectUserById" resultType="map">

select * from tb_user where id = #{id}

</select>

测试

@Test

public void testSelectById() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

Map<String, Object> user = userDao.selectUserById(1);

System.out.println(user);

}

结果可以放到list集合中

方法

//查询指定id的用户信息

List<User> selectUserById(@Param("id") Integer id);

sql

<!-- 查询指定id的用户信息

User selectUserById(@Param("id") Integer id); -->

<select id="selectUserById" resultType="user">

select * from tb_user where id = #{id}

</select>

测试

@Test

public voidtestSelectById() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

List<User> user = userDao.selectUserById(1);

System.out.println(user);

}

3、查询结果为多条记录,用list集合接收

若查询结果有多条表记录

  • 可以用list集合接收

  • 可以用map接收

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

结果可以用list集合接收

结果可以用List集合接收,list中可以是实体类对象,也可以是map

方法:

//查询所有用户数据

List<Map> selectAllUser();

sql:

<!-- 查询所有用户数据 selectAllUser -->

<select id="selectAllUser" resultType="map">

select * from tb_user;

</select>

测试:

@Test

public voidtestSelectAllUser() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

List<Map> user = userDao.selectAllUser();

System.out.println(user);

}

结果一定不能用一个实体类对象接收

结果可以用一个map接收

方法:

/*

* 加上@MapKey注解,此时就可以将每条数据转换的map集合作为值,

* 以某个字段的值作为键,放在同一个map集合中

* 注意这个字段不能重复,一般是主键

*/

@MapKey("id")

Map selectAllUser();

sql

<!-- 查询所有用户数据 selectAllUser -->

<select id="selectAllUser" resultType="map">

select * from tb_user;

</select>

测试:

@Test

public void testSelectAllUser() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

Mapuser = userDao.selectAllUser();

System.out.println(user);

}

特殊sql执行

1、模糊查询

需求:根据用户名模糊查询

方法:

//根据用户名模糊查询

List<User> selectByUserName(@Param("username") String username);

sql

<!-- 根据用户名模糊查询

List<User> selectByUserName

(@Param("username") String username); -->

<select id="selectByUserName" resultType="user">

<!-- select * from tb_user where username like '%${username}%' -->

<!-- select * from tb_user where username like concat('%',#{username},'%') -->

select * from tb_user where username like #{username}

</select>

测试:

@Test

public void testSelectById() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

String username = "张";

List<User> list = userDao.selectByUserName("%"+username+"%");

for (User user : list) {

System.out.println(user);

}

}

2、批量删除

方法:

//根据id批量删除数据 参数如1,2,3

int deletBatch(@Param("ids") String ids);

sql:

<!-- 根据id批量删除数据 参数如1,2,3

int deletBatch(@Param("ids") String ids); -->

<delete id="deletBatch">

delete from tb_user where id in(${ids})

</delete>

测试:

@Test

public voidtestDeleteBatch() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

int rows = userDao.deletBatch("1,3,5");

System.out.println("受影响的行数:"+rows);

}

3、动态设置列名

方法:

//动态设置列

List<User> selectByCols(@Param("cols") String cols);

sql:

<select id="selectByCols" resultType="user">

select ${cols} from tb_user;

</select>

测试:

@Test

public voidtestSelectByCols() {

//通过工具类获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取UserDao接口的实现类对象

UserDao userDao = sqlSession.getMapper(UserDao.class);

//调用方法,执行对应的sql语句

List<User> list = userDao.selectByCols("id,username");

System.out.println(list);

}

4、添加功能获取自增的主键

当添加用户信息是,由于主键自增,我们新增时并不知道新增的数据的主键值时多少,如何在新增时获取这个主键呢?

方法:

//添加用户信息

int insertUser(User user);

sql:

<!-- 添加用户信息int insertUser(@Param("user") User user);

useGeneratedKeys:设置当前标签中sql使用了自增的主键

keyProperty:将自增的主键的值传输到参数中的某个属性中,这里用id属性接收

-->

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">

insert into tb_user values(null,#{username},#{password},#{account})

</insert>

ResultMap自定义映射

准备员工表和部门表,两张表为多对一的关系

1、解决表中字段和实体类中属性不一致

需求:查询所有的员工数据

准备环境:

核心配置文件:

<mappers>

<!-- 引入mapper文件 -->

<mapper resource="mapper/EmpMapper.xml"/>

<mapper resource="mapper/DeptMapper.xml"/>

</mappers>

Emp.java

public class Emp {

private Integer eid; //eid

private String empName;//emp_name

private Integer age; //age

private String job;//job

private Double salary; //salary

//无参+全参+get+set

接口中添加方法

public interface EmpDao {

//查询所有员工

List<Emp> selectAll();

}

mapper文件中添加对应sql

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"" target="_blank">http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cn.niit.dao.EmpDao">

<!-- 查询所有员工 List<Emp> selectAll(); -->

<select id="selectAll" resultType="emp">

select * from emp

</select>

</mapper>

测试:

@Test

public void testSelectAll() {

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//获取EmpDao接口的实现类对象

EmpDao empDao = sqlSession.getMapper(EmpDao.class);

//调用对应方法,执行sql

List<Emp> list = empDao.selectAll();

System.out.println(list);

}

结果:

通过起别名解决

<!-- 查询所有员工 List<Emp> selectAll(); -->

<select id="selectAll" resultType="emp">

select eid,emp_name empName,age,job,salary from emp

</select>

测试结果:

设置驼峰规则

在核心配置文件中配置驼峰映射

<!-- MyBatis全局配置 -->

<settings>

<!-- 可以自动将字段中的_映射为驼峰规则如emp_name:empName -->

<setting name="mapUnderscoreToCamelCase" value="true"/>

</settings>

测试:

<!-- 查询所有员工 List<Emp> selectAll(); -->

<select id="selectAll" resultType="emp">

select * from emp

</select>

通过ResultMap自定义映射规则

<!-- 查询所有员工 List<Emp> selectAll(); -->

<!-- 通过resultMap标签自定义映射规则

id属性设置的是此resultMap的唯一标识

type:设置映射的类型

-->

<resultMap type="emp" id="empResultMap">

<!-- 通过id子标签设置主键的映射关系

通过result标签设置普通字段的映射关系

column设置映射关系中的字段名,必须是表中的字段

property设置映射关系中的属性名,必须是类型中的属性名

-->

<result column="emp_name" property="empName"/>

</resultMap>

<select id="selectAll" resultMap="empResultMap">

select * from emp

</select>

2、处理多对一的映射关系

通过级联属性赋值

修改Emp.java

public class Emp {

private Integer eid; //eid

private String empName;//emp_name

private Integer age; //age

private String job;//job

private Double salary; //salary

//一个员工只对应一个部门

private Dept dept;//对应此员工的部门信息

//无参+全参+get+set+toString

准备Dept.java

public class Dept {

private Integer id;//id

private String deptName; //dept_name

//无参+全参+getset+toString

需求:查询某个员工及其对应的部门信息

添加方法:

//查询某个员工及其对应的部门信息

Emp selectEmpAndDept(@Param("eid") Integer eid);

sql:

<!-- 查询某个员工及其对应的部门信息Emp selectEmpAndDept(@Param("eid") Integer eid); -->

<resultMap type="emp" id="empAndDeptMap">

<result column="id" property="dept.id"/>

<result column="dept_name" property="dept.deptName"/>

</resultMap>

<select id="selectEmpAndDept" resultMap="empAndDeptMap">

select * from emp left join dept

on emp.dept_id=dept.id

where eid=#{eid};

</select>

测试:

@Test

public voidtestSelectEmpAndDept() {

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//获取EmpDao接口的实现类对象

EmpDao empDao = sqlSession.getMapper(EmpDao.class);

//调用对应方法,执行sql

Emp emp = empDao.selectEmpAndDept(1);

System.out.println(emp);

}

结果:

通过association处理多对一映射

<!-- 查询某个员工及其对应的部门信息Emp selectEmpAndDept(@Param("eid") Integer eid); -->

<resultMap type="emp" id="empAndDeptMap">

<id column="eid" property="eid"/>

<result column="emp_name" property="empName"/>

<result column="age" property="age"/>

<result column="job" property="job"/>

<result column="salary" property="salary"/>

<!-- 通过association标签处理多对一的映射关系

property:需要处理映射的属性名

javaType:该属性的类型

-->

<association property="dept" javaType="dept">

<id column="id" property="id"/>

<result column="dept_name" property="deptName"/>

</association>

</resultMap>

<select id="selectEmpAndDept" resultMap="empAndDeptMap">

select * from emp left join dept

on emp.dept_id=dept.id

where eid=#{eid};

</select>

通过分步查询处理多对一映射

第一步:查询对应的员工数据

第二步:拿着查出来的该员工对应的部门id到部门表查对应的部门信息

DeptDao:

public interface DeptDao {

//第二步:拿着查出来的该员工对应的部门id到部门表查对应的部门信息

Dept selectDeptById(@Param("id") Integer id);

}

DeptMapper

<!-- 第二步:拿着查出来的该员工对应的部门id到部门表查对应的部门信息

Dept selectDeptById(@Param("id") Integer id); -->

<select id="selectDeptById" resultType="dept">

select * from dept where id = #{id}

</select>

EmpDao:

//第一步:查询对应的员工数据

Emp selectEmpById(@Param("eid") Integer eid);

sql:

<resultMap type="emp" id="empAndDeptByStepMap">

<id column="eid" property="eid"/>

<result column="emp_name" property="empName"/>

<result column="age" property="age"/>

<result column="job" property="job"/>

<result column="salary" property="salary"/>

<!-- 这次的association,是通过另一个查询去映射

也就是将另一个查询的结果,映射到dept属性上

其中 column:是另一个查询的参数

select:指定另一个查询(通过mapper文件的namespace+sql语句的id)

-->

<association property="dept"

select="cn.niit.dao.DeptDao.selectDeptById"

column="dept_id"/>

</resultMap>

<select id="selectEmpAndDept" resultMap="empAndDeptByStepMap">

select * from emp where eid=#{eid}

</select>

结果:执行了2条sql语句

设置延迟加载

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

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

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

只要保证lazyLoadingEnabled的值为true,aggressiveLazyLoading的值为false,就可以实现按需加载,获取的数据是什么,就会执行相应的sql。如果某条sql有特殊需求,可以通过设置association和collection标签上的fetchType属性,设置当前的分步查询是否使用延迟加载,fetchTypt=lazy则延迟加载,fetchType=eager则立即加载。

修改核心配置文件:

<!-- MyBatis全局配置 -->

<settings>

<!-- 可以自动将字段中的_映射为驼峰规则如emp_name:empName -->

<setting name="mapUnderscoreToCamelCase" value="true"/>

<!-- 设置延迟加载 -->

<setting name="lazyLoadingEnabled" value="true"/>

</settings>

测试:

@Test

public voidtestSelectEmpAndDept() {

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//获取EmpDao接口的实现类对象

EmpDao empDao = sqlSession.getMapper(EmpDao.class);

//调用对应方法,执行sql

Emp emp = empDao.selectEmpAndDept(1);

System.out.println(emp.getEmpName());

}

如果某条分步查询的语句,不希望延迟加载:

<resultMap type="emp" id="empAndDeptByStepMap">

<id column="eid" property="eid"/>

<result column="emp_name" property="empName"/>

<result column="age" property="age"/>

<result column="job" property="job"/>

<result column="salary" property="salary"/>

<!-- 这次的association,是通过另一个查询去映射

也就是将另一个查询的结果,映射到dept属性上

其中 column:是另一个查询的参数

select:指定另一个查询(通过mapper文件的namespace+sql语句的id)

-->

<association property="dept"

select="cn.niit.dao.DeptDao.selectDeptById"

column="dept_id" fetchType="eager"/>

</resultMap>

<select id="selectEmpAndDept" resultMap="empAndDeptByStepMap">

select * from emp where eid=#{eid}

</select>

3、处理一对多的映射关系

需求:查询某个部门及其对应的所有员工数据

修改Dept.java

public class Dept {

private Integer id;//id

private String deptName; //dept_name

//一个部门对应多个员工

private List<Emp> emps;//表示部门对应的所有员工数据

//无参+全参+getset+toString

通过collection处理一对多映射

DeptDao:

//查询某个部门及其对应的所有员工

Dept selectDeptAndEmp(@Param("id") Integer id);

DeptMapper

<!-- 查询某个部门及其对应的所有员工

Dept selectDeptAndEmp(@Param("id") Integer id) -->

<resultMap type="dept" id="DeptAndEmpMap">

<id column="id" property="id"/>

<result column="dept_name" property="deptName"/>

<!-- 通过collection来处理emps属性,ofType:对应的类型是emp(别名) -->

<collection property="emps" ofType="emp">

<id column="eid" property="eid"/>

<result column="emp_name" property="empName"/>

<result column="age" property="age"/>

<result column="job" property="job"/>

<result column="salary" property="salary"/>

</collection>

</resultMap>

<select id="selectDeptAndEmp" resultMap="DeptAndEmpMap">

select * from dept left join emp

on dept.id=emp.dept_id

where dept.id = #{id}

</select>

通过分步查询处理一对多映射

需求:查询某个部门及其所有员工的数据

第一步:查询对应部门的数据如查询1号部门的信息

第二步:拿着部门id到员工表中查询对应的员工信息

EmpDao:

//第二步:拿着部门id到员工表中查询对应的员工信息

List<Emp> selectDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);

EmpMapper

<!-- 第二步:拿着部门id到员工表中查询对应的员工信息

List<Emp> selectDeptAndEmpByStepTwo(@Param("deptId") Integer deptId); -->

<select id="selectDeptAndEmpByStepTwo" resultType="emp">

select * from emp where dept_id = #{deptId}

</select>

DeptDao

//第一步:查询对应部门的数据

Dept selectDeptAndEmpByStepOne(@Param("id") Integer id);

DeptMapper

<resultMap type="dept" id="DeptAndEmpByStepOneMap">

<id column="id" property="id"/>

<result column="dept_name" property="deptName"/>

<!-- 通过collection来处理emps属性,设置拿着哪一列的值作为参数

去执行哪条sql,将结果映射到此emps属性中

-->

<collection property="emps"

select="cn.niit.dao.EmpDao.selectDeptAndEmpByStepTwo"

column="id"/>

</resultMap>

<!-- 第一步:查询对应部门的数据

Dept selectDeptAndEmpByStepOne(@Param("id") Integer id); -->

<select id="selectDeptAndEmpByStepOne" resultMap="DeptAndEmpByStepOneMap">

select * from dept where id = #{id}

</select>

测试:

@Test

public voidtestSelectDeptAndEmp2() {

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//获取EmpDao接口的实现类对象

DeptDao deptDao = sqlSession.getMapper(DeptDao.class);

//调用对应方法,执行sql

Dept dept = deptDao.selectDeptAndEmpByStepOne(1);//查1号部门及其员工数据

System.out.println(dept);

}

结果:

动态SQL

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

1、if标签

mybatis中的if元素用于对某一字段进行判断,从而决定是否执行包含在其中的SQL片段。

需求:

查询emp表中所有员工的信息,另:

如果传递了minSal(最低薪资)和maxSal(最高薪资),则查询薪资大于minSal和小于maxSal的员工信息;

如果只传递了minSal,则查询薪资大于minSal的所有员工信息;

如果只传递了maxSal,则查询薪资小于maxSal的所有员工信息

接口中的方法:

public interface EmpDao {

//根据多条件查询员工数据

List<Emp> selectEmpByMoreSal(@Param("minSal") Double minSal,@Param("maxSal") Double maxSal);

}

sql:

<!-- 根据多条件查询员工数据

List<Emp> selectEmpByMoreSal

(@Param("minSal") Double minSal,@Param("maxSal") Double maxSal);

如果minSal和maxSal都没传 : select * from emp

如果只传了minSal : select * from emp where salary > minSal

如果只传了maxSal : select * from emp where salary < maxSal

如果传了minSal和maxSal : select * from emp where salary > minSal and salary < maxSal

通过动态SQL标签if,实现,用1条SQL动态地实现这四个需求

if标签,可以判断,如果条件为true则if标签内的sql片段会生效,否则不生效

注意需要用&lt;表示<

-->

<select id="selectEmpByMoreSal" resultType="emp">

select * from emp where 1=1

<if test="minSal != null and minSal != ''">

<!-- test表达式的结果为true,if标签内的sql才会生效 -->

salary > #{minSal}

</if>

<if test="maxSal != null and maxSal != ''">

<!-- test表达式的结果为true,if标签内的sql才会生效 -->

and salary &lt; #{maxSal}

</if>

</select>

2、where标签

where元素则用于对包含在其中的SQL语句进行检索,需要时会添加where关键字,还会剔除多余的连接词(比如and或者or),但是,where标签只能剔除内容前的and或or。

<select id="selectEmpByMoreSal" resultType="emp">

select * from emp

<where>

<if test="minSal != null and minSal != ''">

<!-- test表达式的结果为true,if标签内的sql才会生效 -->

salary > #{minSal}

</if>

<if test="maxSal != null and maxSal != ''">

<!-- test表达式的结果为true,if标签内的sql才会生效 -->

and salary &lt; #{maxSal}

</if>

</where>

</select>

3、trim标签

prefix|suffix : 在trim标签内容前后添加指定内容

suffixOverrides|prefixOverrides : 在trim标签内容前后去掉指定内容

可以去除前面的连接词:

<select id="selectEmpByMoreSal" resultType="emp">

select * from emp

<trim prefix="where" prefixOverrides="and|or">

<if test="minSal != null and minSal != ''">

<!-- test表达式的结果为true,if标签内的sql才会生效 -->

salary > #{minSal}

</if>

<if test="maxSal != null and maxSal != ''">

<!-- test表达式的结果为true,if标签内的sql才会生效 -->

and salary &lt; #{maxSal}

</if>

</trim>

</select>

也可以去掉后面的连接词:

<select id="selectEmpByMoreSal" resultType="emp">

select * from emp

<trim prefix="where" suffixOverrides="and|or">

<if test="minSal != null and minSal != ''">

<!-- test表达式的结果为true,if标签内的sql才会生效 -->

salary > #{minSal} and

</if>

<if test="maxSal != null and maxSal != ''">

<!-- test表达式的结果为true,if标签内的sql才会生效 -->

salary &lt; #{maxSal}

</if>

</trim>

</select>

4、choose、when、otherwise

这是一组标签,相当于if..else if… else

和前面不同的是,它只会执行其中某一个sql片段

<select id="selectEmpByMoreSal" resultType="emp">

select * from emp

<trim prefix="where" suffixOverrides="and|or">

<choose>

<when test="minSal != null and minSal != ''">

salary > #{minSal}

</when>

<when test="maxSal != null and maxSal != ''">

salary &lt; #{maxSal}

</when>

<otherwise>

salary > 3000

</otherwise>

</choose>

</trim>

</select>

when至少一个,otherwise最多一个

choose中的sql片段,最多只会选择其中一个执行

5、for-each标签

  • collection属性:设置需要循环的数组或集合

  • item属性:表示数组或集合中的每一个元素

  • separator:循环体之间的分割符

  • open:foreach标签所循环的所有内容的开始符

  • close:foreach标签所循环的所有内容的的结束符

实现批量删除

方法:

//实现批量删除

int deleteBatch(@Param("ids") Integer[] ids);

sql:

<!-- 实现批量删除int deleteBatch(@Param("ids") Integer[] ids); -->

<delete id="deleteBatch">

delete from emp where eid in

<foreach collection="ids" item="id"

separator="," open="(" close=")">

#{id}

</foreach>

</delete>

测试:

@Test

public void testDeleteBatch(){

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

EmpDao empDao = sqlSession.getMapper(EmpDao.class);

int rows = empDao.deleteBatch(new Integer[]{1,2});

System.out.println("受影响的行数:"+rows);

}

通过or实现批量删除

<!-- 实现批量删除int deleteBatch(@Param("ids") Integer[] ids); -->

<delete id="deleteBatch">

delete from emp where

<foreach collection="ids" item="id" separator="or" >

eid = #{id}

</foreach>

</delete>

实现批量添加

方法:

//实现批量添加

intinsertBatch(@Param("emps") List<Emp> emps);

sql:

<!-- 实现批量添加int insertBatch(@Param("emps") List<Emp> emps); -->

<insert id="insertBatch">

insert into emp values

<foreach collection="emps" item="emp" separator=",">

(null,#{emp.empName},#{emp.age},#{emp.job},#{emp.salary},null)

</foreach>

</insert>

6、sql标签

通过sql标签设置sql片段

通过include标签引用sql片段

<sql id="empCols">

eid,emp_name,age,job,salary,dept_id

</sql>

<select id="selectEmpByMoreSal" resultType="emp">

select <include refid="empCols"/> from emp

<trim prefix="where" suffixOverrides="and|or">

<choose>

<when test="minSal != null and minSal != ''">

salary > #{minSal}

</when>

<when test="maxSal != null and maxSal != ''">

salary &lt; #{maxSal}

</when>

<otherwise>

salary > 3000

</otherwise>

</choose>

</trim>

</select>

MyBatis的缓存

1、MyBatis一级缓存

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

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

  • 不是同一个SqlSession

  • 是同一个SqlSession但是查询条件不同

  • 是同一个SqlSession,但是两次查询期间执行了任何一次的增删改操作

  • 是同一个SqlSession但是两次查询期间,手动清空了缓存

测试:

一级缓存是默认开启的,只要是同一个SqlSession,执行相同查询时,第二次不会访问数据库执行sql,而是直接从缓存中获取。

方法:

//根据id查询员工数据

Emp selectById(@Param("id") Integer id);

sql:

<!-- 根据id查询员工数据Emp selectById(@Param("id") Integer id); -->

<select id="selectById" resultType="emp">

select <include refid="empCols"/> from emp where eid = #{id}

</select>

测试:

@Test

public voidtestCache() {

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//通过sqlSession获取empDao1,执行同一条查询语句

EmpDao empDao1 = sqlSession.getMapper(EmpDao.class);

Emp emp1 = empDao1.selectById(5);

//通过同一个sqlSession获取empDao2,执行同一条查询语句

EmpDao empDao2 = sqlSession.getMapper(EmpDao.class);

Emp emp2 = empDao2.selectById(5);

//打印结果

System.out.println(emp1);

System.out.println(emp2);

}

结果:

两次查询都有结果,但是只执行了一次sql语句,第二次查询的结果是从缓存获取的。

测试不同sqlSession,缓存失效

@Test

public voidtestCache() {

//获取两个不同的sqlSession

SqlSession sqlSession1 = SqlSessionUtil.getSqlSession("mybatis-config.xml");

SqlSession sqlSession2 = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//获取接口对象,执行对应sql

EmpDao empDao1 = sqlSession1.getMapper(EmpDao.class);

EmpDao empDao2 = sqlSession2.getMapper(EmpDao.class);

//执行相同sql

Emp emp1 = empDao1.selectById(1);

Emp emp2 = empDao2.selectById(1);

//打印结果

System.out.println(emp1);

System.out.println(emp2);

}

结果:

测试两次查询间进行了新增员工信息的操作:

方法:

//新增员工信息

int insertEmp(Emp emp);

sql:

<!-- 新增员工信息int insertEmp(Empemp); -->

<insert id="insertEmp">

insert into emp values(null,#{empName},#{age},#{job},#{salary},null)

</insert>

测试:

@Test

public voidtestCache() {

//获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//获取接口对象,执行对应sql

EmpDao empDao = sqlSession.getMapper(EmpDao.class);

//同一个SqlSession的情况下执行相同sql

Emp emp1 = empDao.selectById(1);

//两次查询直接做新增操作,则缓存失效

empDao.insertEmp(new Emp(null, "孙策", 18, "销售", 3000.0));

Emp emp2 = empDao.selectById(1);

//打印结果

System.out.println(emp1);

System.out.println(emp2);

}

结果:

数据有了改变,为了保证数据的正确,缓存会失效,所以第二次查询查的是数据库

测试手动清空缓存:

@Test

public void testCache() {

//获取sqlSession

SqlSession sqlSession = SqlSessionUtil.getSqlSession("mybatis-config.xml");

//获取接口对象,执行对应sql

EmpDao empDao = sqlSession.getMapper(EmpDao.class);

//同一个SqlSession的情况下执行相同sql

Emp emp1 = empDao.selectById(5);

//手动清空缓存

sqlSession.clearCache();

Emp emp2 = empDao.selectById(5);

//打印结果

System.out.println(emp1);

System.out.println(emp2);

}

2、MyBatis二级缓存

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

二级缓存开启的条件:

  • 在核心配置文件中,设置全局配置属性cacheEnabled=true,默认为true,不用配置

  • 在映射文件中设置<cache/>标签

  • 二级缓存必须在SqlSession关闭或者提交之后有效

  • 查询的数据所转换的实体类类型必须实现序列化接口

使二级缓存失效的情况:

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

在映射文件中设置<cache/>标签:

<mapper namespace="cn.niit.dao.EmpDao">

<cache/>

实体类实现序列化接口:

public classEmpimplementsSerializable{

private Integer eid; //eid

private String empName;//emp_name

private Integer age; //age

private String job;//job

private Double salary; //salary

测试:

@Test

public void testCache2() {

try {

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

//获取相同的sqlSessionFactory

SqlSessionFactory fac = builder.build(in);

//获取不同的sqlSession

SqlSession sqlSession1 = fac.openSession(true);

SqlSession sqlSession2 = fac.openSession(true);

//获取接口的实现类对象

EmpDao empDao1 = sqlSession1.getMapper(EmpDao.class);

EmpDao empDao2 = sqlSession2.getMapper(EmpDao.class);

//执行相同sql

Emp emp1 = empDao1.selectById(5);

//sqlSession1执行完此sql必须提交或关闭,才会将查询的数据提交到缓存中

//sqlSession1.commit();

sqlSession1.close();

Emp emp2 = empDao2.selectById(5);

//打印结果

System.out.println(emp1);

System.out.println(emp2);

} catch (IOException e) {

e.printStackTrace();

}

}

3、缓存查询的顺序

先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。

如果二级缓存没有命中,再查询一级缓存

如果一级缓存也没有命中,则查询数据库

SqlSession关闭或提交后之后,一级缓存中的数据会写入二级缓存

4、二级缓存的相关配置

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

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

LRU(Least RecentlyUsed) – 最近最少使用:移除最长时间不被使用的对象。

FIFO(First in Firstout) – 先进先出:按对象进入缓存的顺序来移除它们。

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

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

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

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

readOnly属性:只读, true/false

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

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

MyBatis的逆向工程

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

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

  • java实体类

  • Dao接口

  • Mapper映射文件

1、创建逆向工程的步骤

添加mybatis-generator插件pom

<!-- 控制maven在构建过程中的相关配置 -->

<build>

<!-- 构建过程中用到的插件 -->

<plugins>

<!-- 具体的插件 -->

<plugin>

<groupId>org.mybatis.generator</groupId>

<artifactId>mybatis-generator-maven-plugin</artifactId>

<version>1.3.2</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.5.5</version>

</dependency>

<!-- mysql驱动 -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.32</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"

"" target="_blank">http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

<!-- targetRuntime:执行生成的逆向工程的版本

MyBatis3Simple:生成基本的CRUD(清新简洁版)

MyBatis3:生成带条件的CRUD(奢华尊享版)

-->

<context id="DB2Tables" targetRuntime="MyBatis3">

<!--数据库链接URL,用户名、密码yonghedb表示数据库名称,用户和密码别填错了! -->

<jdbcConnection

driverClass="com.mysql.jdbc.Driver"

connectionURL="jdbc:mysql://localhost:3306/shenmu?characterEncoding=utf-8"

userId="root"

password="root">

</jdbcConnection>

<!-- 生成实体类的包名和位置,路径可自定义指定 -->

<javaModelGenerator targetPackage="cn.niit.pojo"

targetProject=".\src\main\java">

<property name="enableSubPackages" value="true" />

<property name="trimStrings" value="true" />

</javaModelGenerator>

<!-- 生成映射文件的包名和位置,路径可自定义指定 -->

<sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources">

<property name="enableSubPackages" value="true" />

</sqlMapGenerator>

<!-- 生成接口的包名和位置,路径可自定义指定 -->

<javaClientGenerator type="XMLMAPPER"

targetPackage="cn.niit.dao" targetProject=".\src\main\java">

<property name="enableSubPackages" value="true" />

</javaClientGenerator>

<!-- 逆向分析的表tableName是数据库中的表名或视图名,可设置为*对应所有表,此时不写domainObjectName

domainObjectName是实体类名 -->

<table tableName="emp" domainObjectName="Emp"/>

<table tableName="dept" domainObjectName="Dept"/>

</context>

</generatorConfiguration>

执行插件

输入如下内容后点击run

mybatis-generator:generate

成功后右键项目刷新即可

2、把生成的文件删掉,测试QBC查询

为了测试,准备mybatis的环境

添加依赖:

<dependencies>

<!-- junit单元测试 -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

</dependency>

<!-- mybatis -->

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>3.5.11</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>1.6.4</version>

</dependency>

<!-- mysql驱动 -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.32</version>

</dependency>

</dependencies>

准备mybayis核心配置文件,log4j.properties,jdbc.properties

这些都是自动生成的

其中Example.java文件是为多条件查询服务的。

测试查询所有员工数据

@Test

public void testMBG(){

try {

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

SqlSession sqlSession = factory.openSession(true);

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

//查询所有数据

List<Emp> emps = empMapper.selectByExample(null);

System.out.println(emps);//记得生成toString才能看到属性值

} catch (IOException e) {

e.printStackTrace();

}

}

测试多条件查询

查询姓名为张三,并且职位为程序员的员工信息

@Test

public void testMBG(){

try {

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

SqlSession sqlSession = factory.openSession(true);

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

//设置条件EmpExample

EmpExample empExample = new EmpExample();

empExample.createCriteria().andEmpNameEqualTo("张三").andJobEqualTo("程序员");

//执行多条件查询

List<Emp> emps = empMapper.selectByExample(empExample);

System.out.println(emps);

} catch (IOException e) {

e.printStackTrace();

}

}

测试:查询姓名为张三,并且职位为程序员 或者 薪资大于等于3000 的员工信息

@Test

public void testMBG(){

try {

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

SqlSession sqlSession = factory.openSession(true);

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

//设置条件EmpExample

EmpExample empExample = new EmpExample();

//姓名为张三且职位为程序员

empExample.createCriteria().andEmpNameEqualTo("张三").andJobEqualTo("程序员");

//或者薪资大于等于3000

empExample.or().andSalaryGreaterThanOrEqualTo(3000.0);

//执行多条件查询

List<Emp> emps = empMapper.selectByExample(empExample);

System.out.println(emps);

} catch (IOException e) {

e.printStackTrace();

}

}

测试更新员工数据

//无参全参构造

public Emp() {

super();

}

public Emp(Integer eid, String empName, Integerage, String job, Double salary, Integer deptId) {

super();

this.eid = eid;

this.empName = empName;

this.age = age;

this.job = job;

this.salary = salary;

this.deptId = deptId;

}

测试updateByPrimaryKeySelective

@Test

public void testMBG(){

try {

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

SqlSession sqlSession = factory.openSession(true);

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

//修改员工数据,不想改的列属性给null值即可

int rows = empMapper.updateByPrimaryKeySelective(new Emp(6, "孙权", null, null, 4000.0, 1));

System.out.println("影响的行数:"+rows);

} catch (IOException e) {

e.printStackTrace();

}

}

测试updateByPrimaryKey

@Test

public void testMBG(){

try {

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

SqlSession sqlSession = factory.openSession(true);

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

//修改员工数据,全都会改,给null值的地方,对应列会改为null值

int rows = empMapper.updateByPrimaryKey(new Emp(6, "孙权", null, null, 4000.0, 1));

System.out.println("影响的行数:"+rows);

} catch (IOException e) {

e.printStackTrace();

}

}

分页插件PageHelper

1、环境布置

1)添加依赖

<dependency>

<groupId>com.github.pagehelper</groupId>

<artifactId>pagehelper</artifactId>

<version>5.3.2</version>

</dependency>

2)在mybatis核心配置文件中配置分页插件

<plugins>

<plugin interceptor="com.github.pagehelper.PageInterceptor"/>

</plugins>

2、使用分页插件

1)要在查询之前开启分页

Page<Object> page =PageHelper.startPage(pageNum, pageSize);

2)查询后可以拿到分页信息pageInfo

PageInfo<Emp> pageInfo = newPageInfo<>(list, navigatePages);

list表示分页数据

navigatePages表示导航分页的数量

/*

* 回忆分页查询

* select * from emp limit startIndex,count;

* limit 可以获取中间数据,

* startIndex表示起始索引,从0开始,表示从哪条sql开始

* count表示要几条数据

* 因此通过limit实现分页查询时,

* pageSize:页面大小,订好的,比如每页放10条数据

* pageNum:当前页的页码

* 分页算法:

* limit (pageSize-1)*pageNum,pageSize

*/

@Test

public void testPageHelper(){

try {

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

SqlSession sqlSession = factory.openSession(true);

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

//查询员工数据,分页查询

PageHelper.startPage(2, 5);//2表示要查询第几页的数据,5表示页面大小

List<Emp> emps = empMapper.selectByExample(null);

System.out.println(emps);

} catch (IOException e) {

e.printStackTrace();

}

}

查看page中的内容

@Test

public void testPageHelper(){

try {

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

SqlSession sqlSession = factory.openSession(true);

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

//查询员工数据,分页查询

Page<Object> page = PageHelper.startPage(2, 5);//2表示要查询第几页的数据,5表示页面大小

System.out.println(page);//看下page中的内容

List<Emp> emps = empMapper.selectByExample(null);

System.out.println(emps);

} catch (IOException e) {

e.printStackTrace();

}

}

Page{count=true, pageNum=2,pageSize=5, startRow=5, endRow=10,

total=41, pages=9, reasonable=false,pageSizeZero=false}

查看pageInfo中的内容:

@Test

public voidtestPageHelper(){

try {

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

SqlSession sqlSession = factory.openSession(true);

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

//查询员工数据,分页查询

Page<Object> page = PageHelper.startPage(2, 5);//2表示要查询第几页的数据,5表示页面大小

//System.out.println(page);//看下page中的内容

List<Emp> emps = empMapper.selectByExample(null);

//查询后可以通过结果,获取一个对应的pageInfo对象

PageInfo<Emp> pageInfo = new PageInfo<>(emps, 5);

System.out.println(pageInfo);

//System.out.println(emps);

} catch (IOException e) {

e.printStackTrace();

}

}

PageInfo{pageNum=2, pageSize=5, size=5(真实数据数量),

startRow=6, endRow=10,

total=41, pages=9, prePage=1,nextPage=3,

isFirstPage=false, isLastPage=false,hasPreviousPage=true, hasNextPage=true,navigatePages=5,navigateFirstPage=1,navigateLastPage=5,

navigatepageNums=[1, 2, 3, 4, 5]}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值