前言
SSM框架的最后一部分
简介
mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精 去处理加载驱动、创建连接、创建statement等繁杂的过程。
mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。
最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
快速开发
步骤:
- 添加MyBatis的坐标
- 创建
user
数据表 - 编写
User
实体类 - 编写映射文件
UserMapper.xml
- 编写核心文件
SqlMapConfig.xml
- 编写测试类
用Maven新建一个项目,结构如下:
编辑pom.xml配置文件,添加MyBatis依赖:
代码语言:javascript复制<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
在MySQL数据库中新建test
库,在其中新建user
表,分别创建id
(int,主键)、username
(varchar)、password
(varchar)字段,并添加数据:
在/src/main/java
目录下继续创建/quick/domain
子目录,在其中编写User
类:
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{"
"id=" id
", username='" username '''
", password='" password '''
'}';
}
}
在/src/main/resources
目录下创建quick/mapper
子目录,在其中编写UserMapper.xml
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<select id="findAll" resultType="quick.domain.User">
select * from user
</select>
</mapper>
代码语言:javascript复制表头信息如下:
<?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">
- 映射文件DTD约束头:
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>
:根标签namespace
:命名空间,与下面子标签语句的id一起组成查询的标识
- 子标签:
<select>
、<insert>
、<update>
、<delete>
- id:与上面的命名空间一起组成查询的标识
resultType
:查询结果对应的实体类型,指向的创建的实体类
在/src/main/resources
目录下编写sqlMapConfig.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--数据源环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<mapper resource="quick/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
代码语言:javascript复制表头信息如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
在/src/test/java
目录下创建test
子目录,在其中编写MyBatisTest
测试类:
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import quick.domain.User;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyBatisTest {
@Test
public void test1() throws IOException {
//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace id
List<User> userList = sqlSession.selectList("userMapper.findAll");
//测试
System.out.println(userList);
//释放资源
sqlSession.close();
}
}
注意正确导包
执行测试类,可以看到打印出存储在数据库中的信息:
操作:增删改查
insert插入
- 插入语句使用
<insert>
标签 - 在映射文件中使用
parameterType
属性指定要插入的数据类型 - Sql语句中使用
#{实体属性名}
方式引用实体中的属性值 - 插入操作使用的API是
sqlSession.insert(“命名空间.id”,实体对象);
- 插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务, 即
sqlSession.commit()
编写UserMapper.xml
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<!--插入操作-->
<insert id="save" parameterType="quick.domain.User">
insert into user values(#{id},#{username},#{password})
</insert>
</mapper>
编写MyBatisTest
测试类:
public class MyBatisTest {
@Test
public void test2() throws IOException {
//模拟User对象
User user = new User();
user.setUsername("tom");
user.setPassword("abc");
//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace id
sqlSession.insert("userMapper.save",user);
//MyBatis执行更新操作,需要提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
}
数据库设置id自增,因此这里没有设置user对象的id值
执行,刷新数据库,可以看到添加了“tom”的信息:
update修改
- 修改语句使用
<update>
标签 - 修改操作使用的API是
sqlSession.update(“命名空间.id”,实体对象);
编写UserMapper.xml
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<!--修改操作-->
<update id="update" parameterType="quick.domain.User">
update user set username=#{username},password=#{password} where id=#{id}
</update>
</mapper>
编写MyBatisTest
测试类:
public class MyBatisTest {
@Test
public void test3() throws IOException {
//模拟User对象
User user = new User();
user.setId(7);
user.setUsername("lucy");
user.setPassword("123");
//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace id
sqlSession.update("userMapper.update",user);
//MyBatis执行更新操作,需要提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
}
执行,刷新数据库,可以看到修改了id为7的信息:
delete删除
- 删除语句使用
<delete>
标签 - Sql语句中使用
#{任意字符串}
方式引用传递的单个参数 - 删除操作使用的API是
sqlSession.delete(“命名空间.id”,Object);
编写UserMapper.xml
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<!--删除操作-->
<delete id="delete" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
</mapper>
编写MyBatisTest
测试类:
public class MyBatisTest {
@Test
public void test4() throws IOException {
//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace id
sqlSession.delete("userMapper.delete",7);
//MyBatis执行更新操作,需要提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
}
测试,刷新数据库,可以看到id为7的数据被删除:
select查询
查询所有
- 查询语句使用
<select>
标签 - 查询操作使用的API是
sqlSession.selectList(“命名空间.id”);
编写UserMapper.xml
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<!--查询操作-->
<select id="findAll" resultType="quick.domain.User">
select * from user
</select>
</mapper>
编写MyBatisTest
测试类:
public class MyBatisTest {
@Test
public void test1() throws IOException {
//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace id
List<User> userList = sqlSession.selectList("userMapper.findAll");
//测试
System.out.println(userList);
//释放资源
sqlSession.close();
}
}
测试,打印查询结果:
查询单个
- 查询语句使用
<select>
标签 - 查询操作使用的API是
sqlSession.selectOne(“命名空间.id”,Object);
编写UserMapper.xml
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<!--根据id查询操作-->
<select id="findById" resultType="user" parameterType="java.lang.Integer">
select * from user where id=#{id}
</select>
</mapper>
编写MyBatisTest
测试类:
public class MyBatisTest {
@Test
public void test5() throws IOException {
//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace id
User user = sqlSession.selectOne("userMapper.findById", 1);
//测试
System.out.println(user);
//释放资源
sqlSession.close();
}
}
测试,打印查询结果:
核心配置文件
层级关系
- configuration配置
- properties属性
- settings设置
- typeAliases类型别名
- typeHandlers类型处理器
- objectFactory对象工厂
- plugins插件
- environments环境
- environment环境变量
- transactionManager事务管理器
- dataSource数据源
- environment环境变量
- databaseIdProvider数据库厂商标识
- mappers映射器
常用配置解析
environments标签
以如下数据库环境配置为例:
代码语言:javascript复制<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
说明:
<environments>
支持多环境配置<environments default="development">
:指定默认的环境名称<environment id="development">
:指定当前环境的名称<transactionManager type="JDBC"/>
:指定事务管理类型是JDBC。事务管理器(transactionManager)类型有两种:JDBC
:这个配置就是直接使用JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。MANAGED
:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置 为false来阻止它默认的关闭行为。
<dataSource type="POOLED">
:指定当前数据源类型是连接池。数据源(dataSource)类型有三种:UNPOOLED
:这个数据源的实现只是每次被请求时打开和关闭连接。POOLED
:这种数据源的实现利用“池”的概念将JDBC连接对象组织起来。JNDI
:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
<property>
:基本参数配置
mapper标签
该标签的作用是加载映射的,加载方式有如下几种:
- 使用相对于类路径的资源引用,例如:
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
- 使用完全限定资源定位符(URL),例如:
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
- 使用映射器接口实现类的完全限定类名,例如:
<mapper class="org.mybatis.builder.AuthorMapper"/>
- 将包内的映射器接口实现全部注册为映射器,例如:
<package name="org.mybatis.builder"/>
properties标签
实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件(例如:jdbc.properties)
代码语言:javascript复制jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root
代码语言:javascript复制<!--加载properties配置文件-->
<properties resource="jdbc.properties"></properties>
<!--数据源环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<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>
typeAliases标签
用于定义别名。
编辑sqlMapConfig.xml
配置文件:
<!--定义别名-->
<typeAliases>
<typeAlias type="quick.domain.User" alias="user"></typeAlias>
</typeAliases>
注意:
- 将
quick.domain.User
全限定名称起别名:user
- 该配置有顺序要求,必须放于
<properties>
标签、<settings>
标签之后,<environments>
标签之前
接着就可以在UserMapper.xml
中使用user别名:
<!--查询操作-->
<select id="findAll" resultType="user">
select * from user
</select>
mybatis框架已经设置好了一些常用的类型的别名:
别名 | 数据类型 |
---|---|
string | String |
long | Long |
int | Integer |
double | Double |
boolean | Boolean |
typeHandlers标签
无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成Java类型。下表描述了一些默认的类型处理器:
类型处理器 | Java类型 | JDBC类型 |
---|---|---|
BooleanTypeHandler | java.lang.Boolean, boolean | 数据库兼容的 BOOLEAN |
ByteTypeHandler | java.lang.Byte, byte | 数据库兼容的 NUMERIC 或 BYTE |
ShortTypeHandler | java.lang.Short, short | 数据库兼容的 NUMERIC 或 SHORT INTEGER |
IntegerTypeHandler | java.lang.Integer, int | 数据库兼容的 NUMERIC 或 INTEGER |
LongTypeHandler | java.lang.Long, long | 数据库兼容的 NUMERIC 或 LONG INTEGER |
可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler
接口,或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler
,然后可以选择性地将它映射到一个JDBC类型。
例如需求:一个Java中的Date数据类型,想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
首先搭建环境:
编辑User
类,新添加birthday
变量,并设定getter和setter方法,重新生成toString()
方法:
public class User {
private int id;
private String username;
private String password;
private Date birthday;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{"
"id=" id
", username='" username '''
", password='" password '''
", birthday=" birthday
'}';
}
}
在MySQL数据库中,在test库中user表下新添加birthday
(bigint)字段:
在/src/main/java/quick
目录下创建mapper
文件夹,在其中创建接口UserMapper
:
public interface UserMapper {
public void save(User user);
public User findById(int id);
}
编写UserMapper.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="quick.mapper.UserMapper">
<insert id="save" parameterType="user">
insert into user values(#{id}, #{username}, #{password}, #{birthday})
</insert>
<select id="findById" parameterType="int" resultType="user">
select * from user where id=#{id}
</select>
</mapper>
测试环境搭建完成。
具体的实现步骤:
- 定义转换类继承类
BaseTypeHandler<T>
- 覆盖4个未实现的方法,其中
setNonNullParameter
为java程序设置数据到数据库的回调方法,getNullableResult
为查询时mysql的字符串类型转换成java的Type类型的方法 - 在MyBatis核心配置文件中进行注册
- 测试转换是否正确
在/src/main/java/quick
目录下新建一个handler
文件夹,在其中编写DateTypeHandler
类:
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
public class DateTypeHandler extends BaseTypeHandler<Date> {
//将java类型 转换成 数据库需要的类型
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
long time = date.getTime();
preparedStatement.setLong(i,time);
}
//将数据库中类型 转换成java类型
//resultSet参数 查询出的结果集
//String参数 是要转换的字段名称
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
//获得结果集中需要的数据(long),将其转换成Date类型 返回
long aLong = resultSet.getLong(s);
Date date = new Date(aLong);
return date;
}
//将数据库中类型 转换成java类型
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
long aLong = resultSet.getLong(i);
Date date = new Date(aLong);
return date;
}
//将数据库中类型 转换成java类型
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
long aLong = callableStatement.getLong(i);
Date date = new Date(aLong);
return date;
}
}
在sqlMapConfig.xml
配置文件中注册类型处理器:
<!--注册类型处理器-->
<typeHandlers>
<typeHandler handler="quick.handler.DateTypeHandler"></typeHandler>
</typeHandlers>
编写MyBatisTest
测试类:
public class MyBatisTest {
@Test
public void test6() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟User对象
User user = new User();
user.setUsername("usertest");
user.setPassword("abc");
user.setBirthday(new Date());
//执行保存操作
mapper.save(user);
sqlSession.commit();
sqlSession.close();
}
@Test
public void test7() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findById(8);
System.out.println(user);
sqlSession.commit();
sqlSession.close();
}
}
测试,执行test6()
测试方法,刷新数据库,可以看到java的Date类型数据转换为毫秒数存储起来:
测试,执行test7()
测试方法,在控制台打印输出java的Date类型数据:
plugins标签
MyBatis可以使用第三方的插件来对功能进行扩展,只需要导入相应的坐标。
这里以分页助手PageHelper
为例:它可以是将分页的复杂操作进行封装,使用简单的方式即 可获得分页的相关数据
开发步骤:
- 导入通用PageHelper的坐标
- 在mybatis核心配置文件中配置PageHelper插件
- 测试分页数据获取
在接口UserMapper中新增findAll()
方法:
public interface UserMapper {
public void save(User user);
public User findById(int id);
public List<User> findAll();
}
在UserMapper.xml
中进行配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="quick.mapper.UserMapper">
<insert id="save" parameterType="user">
insert into user values(#{id}, #{username}, #{password}, #{birthday})
</insert>
<select id="findById" parameterType="int" resultType="user">
select * from user where id=#{id}
</select>
<select id="findAll" resultType="user">
select * from user
</select>
</mapper>
在pom.xml
配置文件中导入分页插件PageHelper的坐标:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
在sqlMapConfig.xml
配置文件中配置分页助手插件:
<!--配置分页助手插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"></property>
</plugin>
</plugins>
注意:如果在pom.xml中导入的坐标版本是5.x.x,则这里不需要配置
<property name="dialect" value="mysql"></property>
编写MyBatisTest测试类:
代码语言:javascript复制public class MyBatisTest {
@Test
public void test8() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//设置分页的相关参数:当前页 每页显示的条数
PageHelper.startPage(1,3);
List<User> userList = mapper.findAll();
for (User user : userList) {
System.out.println(user);
}
sqlSession.commit();
sqlSession.close();
}
}
数据库数据如下:
这里设置查询第二页(每页显示三条数据)
运行测试,结果如下:
也可以获取分页相关的数据:
代码语言:javascript复制public class MyBatisTest {
@Test
public void test8() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//设置分页的相关参数:当前页 每页显示的条数
PageHelper.startPage(1,3);
List<User> userList = mapper.findAll();
for (User user : userList) {
System.out.println(user);
}
//获得与分页相关参数
PageInfo<User> pageInfo = new PageInfo<User>(userList);
System.out.println("当前页:" pageInfo.getPageNum());
System.out.println("每页显示条数:" pageInfo.getPageSize());
System.out.println("总条数:" pageInfo.getTotal());
System.out.println("总页数:" pageInfo.getPages());
System.out.println("上一页:" pageInfo.getPrePage());
System.out.println("下一页:" pageInfo.getNextPage());
System.out.println("是否是首页:" pageInfo.isIsFirstPage());
System.out.println("是否是末页:" pageInfo.isIsLastPage());
sqlSession.commit();
sqlSession.close();
}
}
执行测试,打印结果如下:
常用API
工厂构建器SqlSessionFactoryBuilder
常用API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象:
代码语言:javascript复制String resource = "org/mybatis/builder/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
其中, Resources工具类,这个类在org.apache.ibatis.io包中。Resources类帮助从类路径下、文件系统或 一个 web URL中加载资源文件。
工厂对象SqlSessionFactory
SqlSessionFactory有多个方法创建SqlSession实例。常用的有如下两个:
方法 | 解释 |
---|---|
openSession() | 会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提 交该事务,更新操作数据才会持久化到数据库中 |
openSession(boolean autoCommit) | 参数为是否自动提交,如果设置为true,那么不需要手动提交事务 |
会话对象
SqlSession实例在MyBatis中是非常强大的一个类。在这里会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。
执行语句的方法主要有:
代码语言:javascript复制<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
操作事务的方法主要有:
代码语言:javascript复制void commit()
void rollback()
MyBatis的Dao层实现
传统开发方式
在/src/main/java/quick
目录下创建dao
文件夹,在其中编写UserMapper
接口以及对应的实现类UserMapperImpl
:
UserMapper
接口:
import quick.domain.User;
import java.io.IOException;
import java.util.List;
public interface UserMapper {
public List<User> findAll() throws IOException;
}
UserMapperImpl
实现类:
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import quick.dao.UserMapper;
import quick.domain.User;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserMapperImpl implements UserMapper {
public List<User> findAll() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> userList = sqlSession.selectList("userMapper.findAll");
return userList;
}
}
在/src/main/java/quick
目录下创建service
文件夹,在其中创建ServiceDemo
测试类:
import quick.dao.UserMapper;
import quick.dao.impl.UserMapperImpl;
import quick.domain.User;
import java.io.IOException;
import java.util.List;
public class ServiceDemo {
public static void main(String[] args) throws IOException {
//创建dao层对象 (手动创建dao层)
UserMapper userMapper = new UserMapperImpl();
List<User> all = userMapper.findAll();
System.out.println(all);
}
}
手动创建dao层对象,执行,控制台打印查询结果:
代理开发方式
采用Mybatis的代理开发方式实现DAO层的开发,这种方式是主流。
Mapper接口开发方法只需要编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
- Mapper.xml文件中的
namespace
与mapper
接口的全限定名
相同 - Mapper接口方法名和
Mapper.xml
中定义的每个statement
的id
相同 - Mapper接口方法的
输入参数类型
和mapper.xml
中定义的每个sql的parameterType
的类型相同 - Mapper接口方法的
输出参数类型
和mapper.xml
中定义的每个sql的resultType
的类型相同
为了方便对比,删除/src/main/java/quick/dao/impl
目录及其内部的实现类。
编写接口UserMapper
:
import quick.domain.User;
import java.io.IOException;
import java.util.List;
public interface UserMapper {
public List<User> findAll() throws IOException;
public User findById(int id);
}
编写UserMapper.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="quick.dao.UserMapper">
<!--查询操作-->
<select id="findAll" resultType="user">
select * from user
</select>
<!--根据id查询-->
<select id="findById" parameterType="int" resultType="user">
select * from user where id=#{id}
</select>
</mapper>
注意:
resultType="user"
在sqlMapConfig.xml
中配置过别名
编写ServiceDemo
测试类:
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import quick.dao.UserMapper;
import quick.domain.User;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class ServiceDemo {
public static void main(String[] args) throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
System.out.println(all);
User user = mapper.findById(1);
System.out.println(user);
}
}
执行,控制台打印查询结果:
映射文件
动态sql语句
有些时候业务逻辑复杂时,SQL是动态变化的。
if 标签
根据实体类的不同取值,使用不同的SQL语句来进行查询。
在/src/main/java/quick
目录下创建mapper
文件夹,在其中创建接口UserMapper
:
import quick.domain.User;
import java.util.List;
public interface UserMapper {
public List<User> findByCondition(User user);
}
编写UserMapper.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="quick.mapper.UserMapper">
<select id="findByCondition" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
</mapper>
在/src/test/java/test
目录下编写MapperTest
测试类:
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import quick.mapper.UserMapper;
import quick.domain.User;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MapperTest {
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟条件user
User condition1 = new User();
condition1.setId(1);
condition1.setUsername("ZhangSan");
condition1.setPassword("123");
List<User> userList1 = mapper.findByCondition(condition1);
System.out.println(userList1);
User condition2 = new User();
condition2.setId(1);
List<User> userList2 = mapper.findByCondition(condition2);
System.out.println(userList2);
User condition3 = new User();
condition3.setUsername("ZhangSan");
List<User> userList3 = mapper.findByCondition(condition3);
System.out.println(userList3);
User condition4 = new User();
List<User> userList4 = mapper.findByCondition(condition4);
System.out.println(userList4);
}
}
这里模拟了四种条件的User对象,配置文件会根据不同的条件执行不同的SQL语句
运行测试,结果如下:
foreach标签
循环执行sql的拼接操作。(例如:SELECT * FROM USER WHERE id IN (1,2,5)
)
编写UserMapper
接口:
import quick.domain.User;
import java.util.List;
public interface UserMapper {
public List<User> findByIds(List<Integer> ids);
}
编写UserMapper.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="quick.mapper.UserMapper">
<select id="findByIds" parameterType="list" resultType="user">
select * from user
<where>
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
</mapper>
说明:
- collection:代表要遍历的集合元素,注意编写时不要写#{}
- open:代表语句的开始部分
- close:代表结束部分
- item:代表遍历集合的每个元素,生成的变量名
- sperator:代表分隔符
编写MapperTest
测试类:
public class MapperTest {
@Test
public void test2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟ids数据
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
List<User> userList = mapper.findByIds(ids);
System.out.println(userList);
}
}
运行测试,结果如下:
SQL片段抽取
Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
编写UserMapper.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="quick.mapper.UserMapper">
<!--sql语句的抽取-->
<sql id="selectUser">select * from user</sql>
<select id="findByCondition" parameterType="user" resultType="user">
<!--sql语句的引用-->
<include refid="selectUser"></include>
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
<select id="findByIds" parameterType="list" resultType="user">
<!--sql语句的引用-->
<include refid="selectUser"></include>
<where>
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
</mapper>
多表操作
环境搭建:
新建一个Maven项目
pom.xml
配置文件导入坐标:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
创建目录/src/main/java/multi/domain
,在其中编写User
类和Order
类。
User
类:
public class User {
private int id;
private String username;
private String password;
private Date birthday;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{"
"id=" id
", username='" username '''
", password='" password '''
", birthday=" birthday
'}';
}
}
Order
类:
public class Order {
private int id;
private Date ordertime;
private double total;
//当前订单属于哪一个用户
private User user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getOrdertime() {
return ordertime;
}
public void setOrdertime(Date ordertime) {
this.ordertime = ordertime;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Order{"
"id=" id
", ordertime=" ordertime
", total=" total
", user=" user
'}';
}
}
创建目录/src/main/java/multi/mapper
,在其中创建接口UserMapper
和接口OrderMapper
。
接口UserMapper
:
public interface UserMapper {
}
接口OrderMapper
:
public interface OrderMapper {
}
创建目录/src/resources/multi/mapper
,在其中编写配置文件UserMapper.xml
和配置文件OrderMapper.xml
。
配置文件UserMapper.xml
:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="multi.mapper.UserMapper">
</mapper>
配置文件OrderMapper.xml
:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="multi.mapper.OrderMapper">
</mapper>
在/src/resources
目录下编写jdbc.properties
配置文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root
在/src/resources
目录下编写sqlMapConfig.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载properties配置文件-->
<properties resource="jdbc.properties"></properties>
<!--定义别名-->
<typeAliases>
<typeAlias type="multi.domain.User" alias="user"></typeAlias>
<typeAlias type="multi.domain.Order" alias="order"></typeAlias>
</typeAliases>
<!--数据源环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<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 resource="multi/mapper/UserMapper.xml"></mapper>
<mapper resource="multi/mapper/OrderMapper.xml"></mapper>
</mappers>
</configuration>
在MySQL数据库中的test库中,新建:
user
表,分别创建id
(int,主键)、username
(varchar)、password
(varchar)、birthday
(datetime)字段,并添加数据:
orders
表,分别创建id
(int,主键)、ordertime
(varchar)、total
(double)、uid
(int)字段,并添加数据:
创建目录/src/test/java/multi/test
在其中创建测试类MybatisTest
:
public class MybatisTest {
}
一对一查询
用户表和订单表的关系为:一个用户有多个订单,一个订单只从属于一个用户
需求:查询一个订单,与此同时查询出该订单所属的用户
查询语句:select *,o.id oid from orders o,user u where o.uid=u.id
在OrderMapper
接口中编写查询方法:
public interface OrderMapper {
//查询全部的方法
public List<Order> findAll();
}
编写OrderMapper.xml
测试类:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="multi.mapper.OrderMapper">
<resultMap id="orderMap" type="order">
<!--手动指定字段与实体属性的映射关系
column:数据表的字段名称
property:实体的属性名称
-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<result column="uid" property="user.id"></result>
<result column="username" property="user.username"></result>
<result column="password" property="user.password"></result>
<result column="birthday" property="user.birthday"></result>
</resultMap>
<select id="findAll" resultMap="orderMap">
select *,o.id oid from orders o,user u where o.uid=u.id
</select>
</mapper>
编写测试类MybatisTest
:
public class MybatisTest {
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Order> orderList = mapper.findAll();
for (Order order : orderList) {
System.out.println(order);
}
sqlSession.close();
}
}
还可以利用<association>
标签将order实体中的user属性单独拆开写:
<?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="multi.mapper.OrderMapper">
<resultMap id="orderMap" type="order">
<!--手动指定字段与实体属性的映射关系
column:数据表的字段名称
property:实体的属性名称
-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<!--
property:当前实体(order)中的属性名称(private User user)
javaType:当前实体(order)中的属性的类型(User)
-->
<association property="user" javaType="user">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<select id="findAll" resultMap="orderMap">
select *,o.id oid from orders o,user u where o.uid=u.id
</select>
</mapper>
执行测试,看以看到控制台输出所有订单信息(用户所属):
一对多查询
用户表和订单表的关系为:一个用户有多个订单,一个订单只从属于一个用户
需求:查询一个用户,与此同时查询出该用户具有的订单
查询语句:select *,o.id oid from user u, orders o where u.id=o.uid
编写User
类,添加orderList属性及其getter、setter方法,并重写toString()
方法:
public class User {
private int id;
private String username;
private String password;
private Date birthday;
//描述当前用户存在哪些订单
private List<Order> orderList;
public List<Order> getOrderList() {
return orderList;
}
public void setOrderList(List<Order> orderList) {
this.orderList = orderList;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{"
"id=" id
", username='" username '''
", password='" password '''
", birthday=" birthday
", orderList=" orderList
'}';
}
}
编写UserMapper
接口的方法:
public interface UserMapper {
public List<User> findAll();
}
编写UserMapper.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="multi.mapper.UserMapper">
<resultMap id="userMap" type="user">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<!--配置集合信息
property:集合名称
ofType:当前集合中的数据类型
-->
<collection property="orderList" ofType="order">
<!--封装order的数据-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
select *,o.id oid from user u, orders o where u.id=o.uid
</select>
</mapper>
编写MybatisTest
测试类:
public class MybatisTest {
@Test
public void test2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findAll();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
执行测试,可以看到输出用户的所有订单信息:
多对多查询
在MySQL数据库中的test库中,新建:
role
表,分别创建id
(int,主键)、roleName
(varchar)、roleDesc
(varchar)字段,并添加数据:
user_role
表,分别创建user_id
(int)、role_id
(int)字段,并添加数据:
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
需求:查询用户同时查询出该用户的所有角色
查询语句:select * from user u,user_role ur,role r where u.id=ur.user_id and ur.role_id=r.id
在/src/main/java/multi/domain
目录下新建Role
类:
public class Role {
private int id;
private String roleName;
private String roleDesc;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDesc() {
return roleDesc;
}
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
@Override
public String toString() {
return "Role{"
"id=" id
", roleName='" roleName '''
", roleDesc='" roleDesc '''
'}';
}
}
编写User
类:
public class User {
private int id;
private String username;
private String password;
private Date birthday;
//描述当前用户存在哪些订单
private List<Order> orderList;
//描述当前用户具备哪些角色
private List<Role> roleList;
public List<Role> getRoleList() {
return roleList;
}
public void setRoleList(List<Role> roleList) {
this.roleList = roleList;
}
public List<Order> getOrderList() {
return orderList;
}
public void setOrderList(List<Order> orderList) {
this.orderList = orderList;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{"
"id=" id
", username='" username '''
", password='" password '''
", birthday=" birthday
", orderList=" orderList
", roleList=" roleList
'}';
}
}
在UserMapper接口中添加方法:
代码语言:javascript复制public interface UserMapper {
public List<User> findAll();
public List<User> findUserAndRoleAll();
}
在sqlMapConfig.xml配置文件中配置Role的别名:
代码语言:javascript复制<!--定义别名-->
<typeAliases>
<typeAlias type="multi.domain.User" alias="user"></typeAlias>
<typeAlias type="multi.domain.Order" alias="order"></typeAlias>
<typeAlias type="multi.domain.Role" alias="role"></typeAlias>
</typeAliases>
编写UserMapper.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="multi.mapper.UserMapper">
<resultMap id="userRoleMap" type="user">
<!--user的信息-->
<id column="user_id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<!--user内部的roleList信息-->
<collection property="roleList" ofType="role">
<id column="role_id" property="id"></id>
<result column="roleName" property="roleName"></result>
<result column="roleDesc" property="roleDesc"></result>
</collection>
</resultMap>
<select id="findUserAndRoleAll" resultMap="userRoleMap">
select * from user u,user_role ur,role r where u.id=ur.user_id and ur.role_id=r.id
</select>
</mapper>
编写MybatisTest测试类:
代码语言:javascript复制public class MybatisTest {
@Test
public void test3() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userAndRoleAll = mapper.findUserAndRoleAll();
for (User user : userAndRoleAll) {
System.out.println(user);
}
sqlSession.close();
}
}
运行测试,可以看到控制台打印输出User对应的角色描述:
注解开发
注解 | 描述 |
---|---|
@Insert | 实现新增 |
@Update | 实现更新 |
@Delete | 实现删除 |
@Select | 实现查询 |
@Result | 实现结果集封装 |
@Results | 可以与@Result 一起使用,封装多个结果集 |
@One | 可以与@Result 一起使用,封装多个结果集 |
@Many | 实现一对多结果集封装 |
环境搭建:
新建Maven项目:
配置pom.xml依赖:
代码语言:javascript复制<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
新建目录/src/main/java/anno/domain
,在其中编写User
类:
public class User {
private int id;
private String username;
private String password;
private Date birthday;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{"
"id=" id
", username='" username '''
", password='" password '''
", birthday=" birthday
'}';
}
}
新建目录/src/main/java/anno/mapper
,在其中编写接口UserMapper
:
public interface UserMapper {
public void save(User user);
public void update(User user);
public void delete(int id);
public User findById(int id);
public List<User> findAll();
}
新建目录/src/main/resources/anno/mapper
,在其中编写UserMapper.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="anno.mapper.UserMapper">
<insert id="save" parameterType="user">
insert into user values(#{id},#{username},#{password},#{birthday})
</insert>
<update id="update" parameterType="user">
update user set username=#{username},password=#{password} where id=#{id}
</update>
<delete id="delete" parameterType="int">
delete from user where id=#{id}
</delete>
<select id="findById" parameterType="int" resultType="user">
select * from user where id=#{id}
</select>
<select id="findAll" resultType="user">
select * from user
</select>
</mapper>
在/src/main/resources
目录下配置jdbc.properties
:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root
在/src/main/resources
目录下编写sqlMapConfig.xml
配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载properties配置文件-->
<properties resource="jdbc.properties"></properties>
<!--定义别名-->
<typeAliases>
<typeAlias type="anno.domain.User" alias="user"></typeAlias>
</typeAliases>
<!--数据源环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<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 resource="anno/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
创建目录/src/test/java/anno/test
,在其中编写测试类MyBatisTest
:
import anno.domain.User;
import anno.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyBatisTest {
private UserMapper mapper;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testSave() {
User user = new User();
user.setUsername("tom");
user.setPassword("abc");
mapper.save(user);
}
@Test
public void testUpdate() {
User user = new User();
user.setId(10);
user.setUsername("lucy");
user.setPassword("123");
mapper.update(user);
}
@Test
public void testDelete() {
mapper.delete(10);
}
@Test
public void testFindById() {
User user = mapper.findById(1);
System.out.println(user);
}
@Test
public void testFindAll() {
List<User> userList = mapper.findAll();
for (User user : userList) {
System.out.println(user);
}
}
}
执行testSave()
方法,刷新数据库,可以看到添加tom用户的信息:
执行testUpdate()
方法,刷新数据库,可以看到用户tom的信息已修改为lucy:
执行testDelete()
方法,刷新数据库,删除用户lucy(id=10)成功:
执行testFindById()
方法,可以看到控制台打印id=1的用户信息:
执行testFindAll()
方法,可以看到控制台打印所有用户信息:
增删改查
删除目录/src/main/resources/anno
及其子目录
注意:必须删除
UserMapper.xml
配置文件,否则后面会报如下错误:
后面引入注解扫描时会识别错误,误识别原有的xml配置
修改UserMapper
接口,使用注解的方式添加sql语句:
public interface UserMapper {
@Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
public void save(User user);
@Update("update user set username=#{username},password=#{password} where id=#{id}")
public void update(User user);
@Delete("delete from user where id=#{id}")
public void delete(int id);
@Select("select * from user where id=#{id}")
public User findById(int id);
@Select("select * from user")
public List<User> findAll();
}
在sqlMapConfig.xml
配置文件中重新加载映射关系,使其扫描指定包下的注解:
<!--加载映射关系-->
<mappers>
<!--扫描指定接口所在的包-->
<package name="anno.mapper"></package>
</mappers>
最后执行测试,效果一样。(这里不做展示)
复杂映射
之前实现复杂关系映射可以在映射文件中通过配置<resultMap>
来实现。
使用注解开发后,可以使用@Results
注解 ,@Result
注解,@One
注解,@Many
注解组合完成复杂关系的配置。
-
@Results
- 代替的是标签
<resultMap>
- 该注解中可以使用单个@Result注解,也可以使用@Result集合
- 使用格式:
@Results({@Result(),@Result()})
或@Results(@Result())
- 代替的是标签
-
@Resut
- 代替了
<id>
标签和<result>
标签 - @Result中属性介绍:
- column:数据库的列名
- property:需要装配的属性名
- one:需要使用的
@One注解(
@Result(one=@One)()`) - many:需要使用的
@Many
注解(@Result(many=@many)()
)
- 代替了
-
@One
(一对一)- 代替了
<assocation>
标签,是多表查询的关键,在注解中用来指定子查询返回单一对象 - @One注解属性介绍:
- select: 指定用来多表查询的sqlmapper。
使用格式:
@Result(column=" ",property="",one=@One(select=""))
- select: 指定用来多表查询的sqlmapper。
使用格式:
- 代替了
-
@Many
(多对一)- 代替了
<collection>
标签,是多表查询的关键,在注解中用来指定子查询返回对象集合 - 使用格式:
@Result(property="",column="",many=@Many(select=""))
- 代替了
一对一查询
需求:查询一个订单,与此同时查询出该订单所属的用户
在目录/src/main/java/multi/domain
中编写Order
类:
public class Order {
private int id;
private Date ordertime;
private double total;
//当前订单属于哪一个用户
private User user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getOrdertime() {
return ordertime;
}
public void setOrdertime(Date ordertime) {
this.ordertime = ordertime;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Order{"
"id=" id
", ordertime=" ordertime
", total=" total
", user=" user
'}';
}
}
在目录/src/main/java/multi/mapper
中编写接口OrderMapper
:
public interface OrderMapper {
@Select("select *,o.id oid from orders o,user u where o.uid=u.id ")
@Results({
@Result(column = "oid",property = "id"),
@Result(column = "ordertime",property = "ordertime"),
@Result(column = "total",property = "total"),
@Result(column = "uid",property = "user.id"),
@Result(column = "username",property = "user.username"),
@Result(column = "password",property = "user.password"),
@Result(column = "birthday",property = "user.birthday")
})
public List<Order> findAll();
}
代码语言:javascript复制也可以采用如下方法:
public interface OrderMapper {
@Select("select * from orders")
@Results({
@Result(column = "id",property = "id"),
@Result(column = "ordertime",property = "ordertime"),
@Result(column = "total",property = "total"),
@Result(
property = "user", //要封装的属性名称
column = "uid", //根据哪个字段去查询user表的数据
javaType = User.class, //要封装的实体类型
//select属性:代表查询哪个接口的方法获得数据
one = @One(select = "anno.mapper.UserMapper.findById")
)
})
public List<Order> findAll();
}
在/src/test/java/anno/test
目录下编写MyBatisTest2
测试类:
public class MyBatisTest2 {
private OrderMapper mapper;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
mapper = sqlSession.getMapper(OrderMapper.class);
}
@Test
public void test() {
List<Order> orderList = mapper.findAll();
for (Order order : orderList) {
System.out.println(order);
}
}
}
执行测试,结果如下:
一对多查询
需求:查询一个用户,与此同时查询出该用户具有的订单
修改User
类:
public class User {
private int id;
private String username;
private String password;
private Date birthday;
//描述的是当前用户具有的订单
private List<Order> orderList;
public List<Order> getOrderList() {
return orderList;
}
public void setOrderList(List<Order> orderList) {
this.orderList = orderList;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{"
"id=" id
", username='" username '''
", password='" password '''
", birthday=" birthday
", orderList=" orderList
'}';
}
}
编写接口UserMapper
:
public interface UserMapper {
@Select("select * from user")
@Results({
@Result(id=true ,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "password",property = "password"),
@Result(
property = "orderList",
column = "id",
javaType = List.class,
many = @Many(select = "anno.mapper.OrderMapper.findByUid")
)
})
public List<User> findUserAndOrderAll();
}
编写接口OrderMapper
:
public interface OrderMapper {
@Select("select * from orders where uid=#{uid}")
public List<Order> findByUid(int uid);
}
在/src/test/java/anno/test
目录下编写MyBatisTest3
测试类:
public class MyBatisTest3 {
private UserMapper mapper;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void test() {
List<User> userAndOrderAll = mapper.findUserAndOrderAll();
for (User user : userAndOrderAll) {
System.out.println(user);
}
}
}
执行测试,结果如下:
多对多查询
需求:查询用户同时查询出该用户的所有角色
修改User
类:
public class User {
private int id;
private String username;
private String password;
private Date birthday;
//当前用户具备哪些角色
private List<Role> roleList;
//描述的是当前用户具有的订单
private List<Order> orderList;
public List<Order> getOrderList() {
return orderList;
}
public List<Role> getRoleList() {
return roleList;
}
public void setRoleList(List<Role> roleList) {
this.roleList = roleList;
}
public void setOrderList(List<Order> orderList) {
this.orderList = orderList;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{"
"id=" id
", username='" username '''
", password='" password '''
", birthday=" birthday
", roleList=" roleList
", orderList=" orderList
'}';
}
}
在目录/src/main/java/multi/domain
中编写Role
类:
public class Role {
private int id;
private String roleName;
private String roleDesc;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDesc() {
return roleDesc;
}
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
@Override
public String toString() {
return "Role{"
"id=" id
", roleName='" roleName '''
", roleDesc='" roleDesc '''
'}';
}
}
在目录/src/main/java/multi/mapper
中编写接口RoleMapper
:
public interface RoleMapper {
@Select("select * from user_role ur,role r where ur.role_id=r.id and ur.user_id=#{uid}")
public List<Role> findByUid(int uid);
}
编写接口UserMapper
:
public interface UserMapper {
@Select("select * from user")
@Results({
@Result(id = true,column = "id",property = "id"),
@Result(id = true,column = "username",property = "username"),
@Result(id = true,column = "password",property = "password"),
@Result(id = true,column = "birthday",property = "birthday"),
@Result(
property = "roleList",
column = "id",
javaType = List.class,
many = @Many(select = "anno.mapper.RoleMapper.findByUid")
)
})
public List<User> findUserAndRoleAll();
}
在/src/test/java/anno/test
目录下编写MyBatisTest4
测试类:
public class MyBatisTest4 {
private UserMapper mapper;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void test() {
List<User> userAndRoleAll = mapper.findUserAndRoleAll();
for (User user : userAndRoleAll) {
System.out.println(user);
}
}
}
执行测试,结果如下:
后记
个人感觉如果sql语句复杂,还是用xml配置文件的方式便捷,注解的可读性差一些。