Mybatis3
一、Mybatis简介
1.1、原始jdbc操作的分析
原始jdbc开发存在的问题如下
- 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能
- sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变java代码。
- 查询操作时,需要手动将结果集中的数据手动封装到实体中。插入操作时,需要手动将实体的数据设置到sql语句的占位符位置
应对上述问题给出的解决方案
4. 使用数据库连接池初始化连接资源
5. 将sql语句抽取到xml配置文件中
6. 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射
1.2、什么是Mybatis
- mybatis 是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力
去处理加载驱动、创建连接、创建statement等繁杂的过程。 - mybatis通过xml或注解的方式将要执行的各种 statement配置起来,并通过java对象和statement中sql的动态参数进行
映射生成最终执行的sql语句。 - 最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc 进行了
封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
二、Mybatis的快速入门
2.1、环境搭建
1.导入MyBatis的坐标和其他相关坐标
<dependencies>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
2.创建t_user数据表
CREATE TABLE user (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20),
`password` varchar(40),
PRIMARY KEY (`id`)
)
- 编写User实体
@Data
public class User {
private Integer id;
private String username;
private String password;
}
- 编写UserMapper和映射文件
public interface UserMapper {
List<User> findAll();
User findById(int id);
int insert(User user);
int update(User user);
int deelte(int id);
}
<?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="com.java521.pojo.User">
select *
from user
</select>
<insert id="insert" parameterType="com.java521.pojo.User">
insert into user values(#{id}, #{username}, #{password})
</insert>
<update id="update" parameterType="user">
update user set username=#{username}, password=#{password} where id=#{id}
</update>
<delete id="delete" parameterType="integer">
delete from user
where id= #{id}
</delete>
</mapper>
- 编写MyBatis核心文件
<?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>
<typeAliases>
<package name="com.java521.pojo"/>
</typeAliases>
<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://127.0.0.1/user"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/java521/UserMapper.xml"/>
</mappers>
</configuration>
2.2、编写测试代码
public class TestMybatis {
@Test
public void findAll() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> userList = sqlSession.selectList("userMapper.findAll");
System.out.println(userList);
sqlSession.close();
resourceAsStream.close();
}
}
三、MyBatis的映射文件概述
四、MyBatis的增删改查操作
4.1、MyBatis的插入数据操作
1、编写UserMapper映射文件
<insert id="insert" parameterType="com.java521.pojo.User">
insert into user values(#{id}, #{username}, #{password})
</insert>
2、编写插入实体User的代码
@Test
public void insert() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setId(3);
user.setUsername("java");
user.setPassword("123456");
int result = sqlSession.insert("userMapper.insert", user);
System.out.println(result);
//提交事务
sqlSession.commit();
sqlSession.close();
resourceAsStream.close();
}
插入操作注意问题
- 插入语句使用insert标签
- 在映射文件中使用
parameterType
属性指定要插入的数据类型 - sql语句中使用
#{实体属性名}
方式引用实体中的属性值 - 插入操作使用的API是
sqlSession.insert(“命名空间.id”,实体对象);
- 插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务,即
sqlSession.commit()
4.2、MyBatis的修改数据操作
1、编写UserMapper映射文件
<update id="update" parameterType="user">
update user set username=#{username}, password=#{password} where id=#{id}
</update>
2、编写修改实体User的代码
@Test
public void update() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setId(3);
user.setUsername("php");
user.setPassword("666666");
int result = sqlSession.update("userMapper.update", user);
System.out.println(result);
//提交事务
sqlSession.commit();
sqlSession.close();
resourceAsStream.close();
}
修改操作注意问题
- 修改语句使用update标签
- 修改操作使用的API是
sqlSession.update(“命名空间.id”,实体对象);
4.3、MyBatis的删除数据操作
<delete id="delete" parameterType="integer">
delete from user
where id= #{id}
</delete>
@Test
public void delete() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
int id = 3;
int result = sqlSession.delete("userMapper.delete", id);
System.out.println(result);
//提交事务
sqlSession.commit();
sqlSession.close();
resourceAsStream.close();
}
删除操作注意问题
- 删除语句使用delete标签
- sql语句中使用
#{任意字符串}
方式引用传递的单个参数 - 删除操作使用的API是
sqlSession.delete(“命名空间.id”,Object);
五、MyBatis核心配置文件概述
5.1、 MyBatis核心配置文件层级关系
5.2、MyBatis常用配置解析
5.2.1、environments标签
数据库环境的配置,支持多环境配置
事务管理器(transactionManager)类型有两种:
- JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
- MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE
应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置
为 false 来阻止它默认的关闭行为。
数据源(dataSource)类型有三种:
- UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
- JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置
一个 JNDI 上下文的引用。
5.2.2、mapper标签
该标签的作用是加载映射的,加载方式有如下几种:
- 使用相对于类路径的资源引用,例如:
- 使用完全限定资源定位符(URL),例如:
- 使用映射器接口实现类的完全限定类名,例如:
- 将包内的映射器接口实现全部注册为映射器,例如:
5.2.3、Properties标签
实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件
5.2.4、typeAliases标签
类型别名是为Java 类型设置一个短的名字。原来的类型名称配置如下
User全限定名称
<select id="findAll" resultType="com.itheima.domain.User">
select * from User
</select>
配置typeAliases,为com.itheima.domain.User定义别名为user
<typeAliases>
<typeAlias type="com.itheima.domain.User“ alias="user"></typeAlias>
</typeAliases>
user为别名
<select id="findAll" resultType="user">
select * from User
</select>
上面我们是自定义的别名,mybatis框架已经为我们设置好的一些常用的类型的别名
别名 | 数据类型 |
---|---|
string | String |
long | Long |
int | Integer |
double | Double |
boolean | Boolean |
… | … |
5.3、知识小结
1、properties标签:该标签可以加载外部的properties文件
<properties resource="jdbc.properties"></properties>
2、typeAliases标签:设置类型别名
<typeAliases>
<typeAlias type="com.java521.pojo.User" alias="user"></typeAlias>
</typeAliases>
3、mappers标签:加载映射配置
<mappers>
<mapper resource="com/java521/UserMapper.xml"/>
</mappers>
4、environments标签:数据源环境配置标签
<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://127.0.0.1/user"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
六、 MyBatis相应API
6.1、SqlSession工厂构建器SqlSessionFactoryBuilder
常用API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象
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 中加载资源文件
6.2、SqlSession工厂对象SqlSessionFactory
SqlSessionFactory 有多个个方法创建 SqlSession 实例。常用的有如下两个:
方法 | 解释 |
---|---|
openSession() | 会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化到数据库中 |
openSession(boolean autoCommit) | 参数为是否自动提交,如果设置为true,那么不需要手动提交事务 |
6.3、SqlSession会话对象
SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。
执行语句的方法主要有:
<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)
操作事务的方法主要有:
void commit()
void rollback()
七、Mybatis的Dao层实现
7.1、传统开发方式
- 编写UserDao接口
public interface UserDao {
List<User> findAll() throws IOException;
}
- 编写UserDaoImpl实现
public class UserDaoImpl implements UserDao {
@Override
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");
sqlSession.close();
return userList;
}
}
- 测试传统方式
@Test
public void testTraditionDao() throws IOException {
UserDao userDao = new UserDaoImpl();
List<User> all = userDao.findAll();
System.out.println(all);
}
7.2、代理开发方式
1、代理开发方式介绍
采用 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的类型相同
2、编写UserMapper接口
3、测试代理方式
@Test
public void testProxyDao() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获得MyBatis框架生成的UserMapper接口的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findById(1);
System.out.println(user);
sqlSession.close();
}
7.3、知识小结
MyBatis的Dao层实现的两种方式:
- 手动对Dao进行实现:传统开发方式
- 代理方式对Dao进行实现:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
八、MyBatis映射文件深入
8.1、动态SQL
1、动态sql语句概述
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
2、动态 SQL 之<if>
我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果
username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
<select id="findAll" resultType="user">
select *
from user
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="username != null">
and username = #{username}
</if>
</where>
</select>
当查询条件id和username都存在时,控制台打印的sql语句如下:
....
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(1);
user.setUsername("lucy");
List<User> userList = userMapper.findAll(user);
}
当查询条件只有id存在时,控制台打印的sql语句如下:
....
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(1);
List<User> userList = userMapper.findAll(user);
}
3、动态 SQL 之
循环执行sql的拼接操作,例如:SELECT * FROM USER WHERE id IN (1,2,5)
。
<select id="findByIds" parameterType="list" resultType="user">
select * from user
<where>
<foreach collection="array" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
测试代码片段如下:
...
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int[] ids = new int[]{2,5};
List<User> userList = userMapper.findByIds(ids);
3、动态 SQL 之
<foreach>
标签用于遍历集合,它的属性:
- collection:代表要遍历的集合元素,注意编写时不要写#{}
- open:代表语句的开始部分
- close:代表结束部分
- item:代表遍历集合的每个元素,生成的变量名
- sperator:代表分隔符
8.2、SQL片段抽取
Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
<!--抽取sql片段简化编写-->
<sql id="selectUser">
select * from User
</sql>
<select id="findAll" resultType="user">
<include refid="selectUser"></include> w
<where>
<if test="id!=null">
and id = #{id}
</if>
<if test="username!=null">
and username = #{username}
</if>
</where>
</select>
<select id="findById" resultType="user">
<include refid="selectUser"></include> w
where id = #{id}
</select>
8.3、知识小结
MyBatis映射文件配置:
- <select>:查询
- <insert>:插入
- <update>:修改
- <delete>:删除
- <where>:where条件
- <if>:if判断
- <foreach>:循环
- <sql>:sql片段抽取
九、MyBatis核心配置文件深入
9.1、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 或 SMALLINT |
IntegerTypeHandler | java.lang.Integer , int | 数据库兼容的 NUMERIC 或 INTEGER |
LongTypeHandler | java.lang.Long , long | 数据库兼容的 NUMERIC 或 BIGINT |
FloatTypeHandler | java.lang.Float , float | 数据库兼容的 NUMERIC 或 FLOAT |
DoubleTypeHandler | java.lang.Double , double | 数据库兼容的 NUMERIC 或 DOUBLE |
BigDecimalTypeHandler | java.math.BigDecimal | 数据库兼容的 NUMERIC 或 DECIMAL |
StringTypeHandler | java.lang.String | CHAR , VARCHAR |
ClobReaderTypeHandler | java.io.Reader | - |
ClobTypeHandler | java.lang.String | CLOB , LONGVARCHAR |
NStringTypeHandler | java.lang.String | NVARCHAR , NCHAR |
NClobTypeHandler | java.lang.String | NCLOB |
BlobInputStreamTypeHandler | java.io.InputStream | - |
ByteArrayTypeHandler | byte[] | 数据库兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB , LONGVARBINARY |
DateTypeHandler | java.util.Date | TIMESTAMP |
DateOnlyTypeHandler | java.util.Date | DATE |
TimeOnlyTypeHandler | java.util.Date | TIME |
SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP |
SqlDateTypeHandler | java.sql.Date | DATE |
SqlTimeTypeHandler | java.sql.Time | TIME |
ObjectTypeHandler | Any | OTHER 或未指定类型 |
EnumTypeHandler | Enumeration Type | VARCHAR 或任何兼容的字符串类型,用来存储枚举的名称(而不是索引序数值) |
EnumOrdinalTypeHandler | Enumeration Type | 任何兼容的 NUMERIC 或 DOUBLE 类型,用来存储枚举的序数值(而不是名称)。 |
SqlxmlTypeHandler | java.lang.String | SQLXML |
InstantTypeHandler | java.time.Instant | TIMESTAMP |
LocalDateTimeTypeHandler | java.time.LocalDateTime | TIMESTAMP |
LocalDateTypeHandler | java.time.LocalDate | DATE |
LocalTimeTypeHandler | java.time.LocalTime | TIME |
OffsetDateTimeTypeHandler | java.time.OffsetDateTime | TIMESTAMP |
OffsetTimeTypeHandler | java.time.OffsetTime | TIME |
ZonedDateTimeTypeHandler | java.time.ZonedDateTime | TIMESTAMP |
YearTypeHandler | java.time.Year | INTEGER |
MonthTypeHandler | java.time.Month | INTEGER |
YearMonthTypeHandler | java.time.YearMonth | VARCHAR 或 LONGVARCHAR |
JapaneseDateTypeHandler | java.time.chrono.JapaneseDate | DATE |
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个JDBC类型。例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
开发步骤:
- 定义转换类继承类BaseTypeHandler
- 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult
为查询时 mysql的字符串类型转换成 java的Type类型的方法 - 在MyBatis核心配置文件中进行注册
- 测试转换是否正确
public class MyDateTypeHandler extends BaseTypeHandler<Date> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i, date.getTime() + "");
}
@Override
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
return new Date(resultSet.getLong(s));
}
@Override
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
return new Date(resultSet.getLong(i));
}
@Override
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return callableStatement.getDate(i);
}
}
<!--注册类型自定义转换器-->
<typeHandlers>
<typeHandler handler="com.java521.handle.MyDateTypeHandler"></typeHandler>
</typeHandlers>
测试添加操作:
@Test
public void insertHandleType() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsername("siri");
user.setPassword("123456");
user.setBirthday(new Date());
int count = userMapper.insert(user);
System.out.println(count);
//提交事务
sqlSession.commit();
sqlSession.close();
resourceAsStream.close();
}
9.2、plugins标签
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
- 导入通用PageHelper的坐标
- 在mybatis核心配置文件中配置PageHelper插件
- 测试分页数据获取
1、导入通用PageHelper坐标
<!-- 分页助手 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
2、在mybatis核心配置文件中配置PageHelper插件
<!-- 注意:分页助手的插件 配置在通用馆mapper之前 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定方言 -->
<property name="dialect" value="mysql"/>
</plugin>
3、测试分页代码实现
//设置分页参数
PageHelper.startPage(1, 2);
List<User> userList = userMapper.findAll(new User());
System.out.println(userList);
4、获得分页相关的其他参数
//设置分页参数
PageHelper.startPage(1, 2);
List<User> userList = userMapper.findAll(new User());
System.out.println(userList);
//其他分页的数据
PageInfo<User> pageInfo = new PageInfo<User>(userList);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示长度:"+pageInfo.getPageSize());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage());
9.3、知识小结
MyBatis核心配置文件常用标签:
1、properties标签:该标签可以加载外部的properties文件
2、typeAliases标签:设置类型别名
3、environments标签:数据源环境配置标签
4、typeHandlers标签:配置自定义类型处理器
5、plugins标签:配置MyBatis的插件
十、Mybatis多表查询
10.1、一对一查询
1、一对一查询的模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
2、一对一查询的语句
对应的sql语句:select * from orders o,user u where o.uid=u.id;
3、创建Order和User实体
@Data
public class Order {
private int id;
private Date ordertime;
private double total;
//当前订单属于哪一个用户
private User user;
}
@Data
public class User {
private int id;
private String username;
private String password;
private Date birthday;
}
4、创建OrderMapper接口
public interface OrderMapper {
List<Order> findAll();
}
5、配置OrderMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.java521.mapper.OrderMapper">
<resultMap id="orderMap" type="com.java521.pojo.Order">
<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 *
from orders o, user u
where o.uid = u.id
</select>
</mapper>
其中<resultMap>还可以配置如下:
<resultMap id="orderMap" type="com.java521.pojo.Order">
<result property="id" column="id"></result>
<result property="ordertime" column="ordertime"></result>
<result property="total" column="total"></result>
<association property="user" javaType="com.java521.pojo.User">
<result column="uid" property="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
6、测试结果
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
List<Order> orderList = orderMapper.findAll();
for (Order order : orderList) {
System.out.println("order = " + order);
}
10.2、一对多查询
1、一对多查询的模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
2、一对多查询的语句
对应的sql语句:select *,o.id oid from user u left join orders o on u.id=o.uid;
3、修改User实体
@Data
public class Order {
private int id;
private Date ordertime;
private double total;
//当前订单属于哪一个用户
private User user;
}
@Data
public class User {
private int id;
private String username;
private String password;
private Date birthday;
//描述的是当前用户存在哪些订单
private List<Order> orderList;
}
4、创建UserMapper接口
public interface UserMapper {
List<User> findAll();
}
5、配置UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.java521.mapper.UserMapper">
<resultMap id="userMap" type="com.java521.pojo.User">
<result column="id" property="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<collection property="orderList" ofType="com.java521.pojo.Order">
<result column="oid" property="id"></result>
<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
left join orders o on u.id = o.uid
</select>
</mapper>
6、测试结果
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> orderList = userMapper.findAll();
for (User order : orderList) {
System.out.println("order = " + order);
}
10.3、多对多查询
1、多对多查询的模型
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
2、多对多查询的语句
对应的sql语句:select u.*,r.*,r.id rid from user u left join user_role ur on u.id=ur.user_id inner join role r on ur.role_id=r.id;
3、创建Role实体,修改User实体
@Data
public class User {
private int id;
private String username;
private String password;
private Date birthday;
//描述的是当前用户存在哪些订单
private List<Order> orderList;
//描述的是当前用户具备哪些角色
private List<Role> roleList;
}
@Data
public class Role {
private int id;
private String rolename;
}
4、添加UserMapper接口方法
public interface UserMapper {
List<User> findAll();
List<User> findAllUserAndRole();
}
5、配置UserMapper.xml
<resultMap id="userRoleMap" type="com.java521.pojo.User">
<result column="id" property="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<collection property="roleList" ofType="com.java521.pojo.Role">
<result column="rid" property="id"></result>
<result column="rolename" property="rolename"></result>
</collection>
</resultMap>
<select id="findAllUserAndRole" resultMap="userRoleMap">
select u.*, r.*, r.id rid
from user u
left join user_role ur on u.id = ur.user_id
inner join role r on ur.role_id = r.id
</select>
6、 测试结果
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> orderList = userMapper.findAllUserAndRole();
for (User order : orderList) {
System.out.println("order = " + order);
System.out.println("================");
}
10.4、知识小结
MyBatis多表配置方式:
- 一对一配置:使用<resultMap>做配置
- 一对多配置:使用<resultMap>+<collection>做配置
- 多对多配置:使用<resultMap>+<collection>做配置
十一、Mybatis的注解开发
11.1、MyBatis的常用注解
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper
映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
- @Insert:实现新增
- @Update:实现更新
- @Delete:实现删除
- @Select:实现查询
- @Result:实现结果集封装
- @Results:可以与@Result 一起使用,封装多个结果集
- @One:实现一对一结果集封装
- @Many:实现一对多结果集封装
11.2、MyBatis的增删改查
我们完成简单的user表的增删改查的操作
public class Test04 {
private UserMapper userMapper;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
userMapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testAdd() {
User user = new User();
user.setUsername("测试数据");
user.setPassword("123");
user.setBirthday(new Date());
userMapper.add(user);
}
@Test
public void testUpdate() throws IOException {
User user = new User();
user.setId(16);
user.setUsername("测试数据修改");
user.setPassword("abc");
user.setBirthday(new Date());
userMapper.update(user);
}
@Test
public void testDelete() throws IOException {
userMapper.delete(16);
}
@Test
public void testFindById() throws IOException {
User user = userMapper.findById(1);
System.out.println(user);
}
@Test
public void testFindAll() throws IOException {
List<User> all = userMapper.findAll();
for(User user : all){
System.out.println(user);
}
}
}
修改MyBatis的核心配置文件,我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可
<mappers>
<mapper resource="com/java521/UserMapper.xml"/>
</mappers>
或者指定扫描包含映射关系的接口所在的包也可以
<mappers>
<package name="com.java521.mapper"></package>
</mappers>
11.3、MyBatis的注解实现复杂映射开发
实现复杂关系映射之前我们可以在映射文件中通过配置<resultMap>
来实现,使用注解开发后,我们可以使用@Results
注解
,@Result注解
,@One注解
,@Many
注解组合完成复杂关系的配置
注解 | 说明 |
---|---|
@Results | 代替的是标签该注解中可以使用单个@Result注解,也可以使用@Result集 合。。 使用格式:@Results({@Result(),@Result()})或@Results(@Result()) |
@Resut | 代替了标签和标签 @Result中属性介绍: column:数据库的列名 property:需要装配的属性名 one:需要使用的@One 注解(@Result(one=@One)())) many:需要使用的@Many 注解(@Result(many=@many)())) |
@One (一对一) | 代替了 标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。 @One注解属性介绍: select: 指定用来多表查询的 sqlmapper 使用格式:@Result(column=" “,property=”",one=@One(select="")) |
@Many (多对一) | 代替了标签, 是是多表查询的关键,在注解中用来指定子查询返回对象集合。 使用格式:@Result(property="",column="",many=@Many(select="")) |
11.4、一对一查询
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户,一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
@Select("select * from user where id=#{id}")
User findById(int i);
测试结果
@Test
public void testFindById() throws IOException {
User user = userMapper.findById(1);
System.out.println(user);
}
11.5、一对多查询
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户,一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
@Select("select * from user")
@Results({
@Result(id = true,property = "id",column = "id"),
@Result(property = "username",column = "username"),
@Result(property = "password",column = "password"),
@Result(property = "birthday",column = "birthday"),
@Result(property = "orderList",column = "id", javaType = List.class,
many = @Many(select = "com.java521.mapper.OrderMapper.findByUid"))
})
List<User> findAllUserAndOrder();
@Select("select * from orders where uid=#{uid}")
List<Order> findByUid(int uid);
测试结果
@Test
public void testFindAllUserAndOrder() throws IOException {
List<User> all = userMapper.findAllUserAndOrder();
for (User user : all) {
System.out.println(user.getUsername());
List<Order> orderList = user.getOrderList();
for (Order order : orderList) {
System.out.println(order);
}
System.out.println("-----------------------------");
}
}
11.6、多对多查询的模型
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用,多对多查询的需求:查询用户同时查询出该用户的所有角色
@Select("select * from user")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "username", column = "username"),
@Result(property = "password", column = "password"),
@Result(property = "birthday", column = "birthday"),
@Result(property = "roleList", column = "id", javaType = List.class,
many = @Many(select = "com.java521.mapper.RoleMapper.findByUid"))
})
List<User> findAllUserAndRole();
添加RoleMapper ,修改xml
public interface RoleMapper {
@Select("select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=#{uid}")
List<Role> findByUid(int uid);
}
<mappers>
<package name="com.java521.mapper"/>
</mappers>
测试结果
@Test
public void testFindAllUserAndRole() throws IOException {
List<User> all = userMapper.findAllUserAndRole();
for (User user : all) {
System.out.println(user.getUsername());
List<Role> roleList = user.getRoleList();
for (Role role : roleList) {
System.out.println(role);
}
System.out.println("----------------------------------");
}
}