一、mybatis相关概念
- 对象/关系数据库映射(ORM)
ORM全称Object/Relation Mapping:表示对象-关系映射的缩写
ORM完成⾯向对象的编程语⾔到关系数据库的映射。当ORM框架完成映射后,程序员既可以利⽤⾯向
对象程序设计语⾔的简单易⽤性,⼜可以利⽤关系数据库的技术优势。 ORM把关系数据库包装成⾯向对
象的模型。 ORM框架是⾯向对象设计语⾔与关系数据库发展不同步时的中间解决⽅案。采⽤ORM框架
后,应⽤程序不再直接访问底层数据库,⽽是以⾯向对象的⽅式来操作持久化对象,⽽ORM框架则将这
些⾯向对象的操作转换成底层SQL操作。 ORM框架实现的效果:把对持久化对象的保存、修改、删除
等操作,转换为对数据库的操作 - Mybatis简介
MyBatis是⼀款优秀的基于ORM的半⾃动轻量级持久层框架,它⽀持定制化SQL、存储过程以及⾼级映
射。 MyBatis避免了⼏乎所有的JDBC代码和⼿动设置参数以及获取结果集。 MyBatis可以使⽤简单的
XML或注解来配置和映射原⽣类型、接⼝和Java的POJO (Plain Old Java Objects,普通⽼式Java对 象)
为数据库中的记录。 - 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 Objects(DAO) - Mybatis优势
Mybatis是⼀个半⾃动化的持久层框架,对开发⼈员开说,核⼼sql还是需要⾃⼰进⾏优化, sql和java编
码进⾏分离,功能边界清晰,⼀个专注业务,⼀个专注数据。
分析图示如下:
二、Mybatis的基本应用
mybatis快速入门
MyBatis官⽹地址: http://www.mybatis.org/mybatis-3/
开发步骤:
①添加MyBatis的坐标
②创建user数据表
③编写User实体类
④编写映射⽂件UserMapper.xml
⑤编写核⼼⽂件SqlMapConfig.xml
⑥编写Dao、Mapper
实际任选一种即可,推荐mapper,这里演示两种都用了
⑦编写测试类
导⼊MyBatis的坐标和其他相关坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lpy</groupId>
<artifactId>mybatis_quickStarter</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
</dependency>
</dependencies>
</project>
创建user数据表
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`birthday` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
编写User实体
package com.lpy.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @author lipengyu
*/
@Getter
@Setter
@ToString
public class User {
private Integer id;
private String username;
private String password;
private String birthday;
}
编写UserMapper映射⽂件 传统查询namespace为自定义
<?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="user">
select * from user
</select>
<!-- 查询需设置resultType或resultMap 否则会报-->
<!-- A query was run and no Result Maps were found for the Mapped Statement-->
<select id="findById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
<select id="findOneByCondition" resultType="user" parameterType="user">
select * from user where username = #{username} and password = #{password}
</select>
<!-- insert想返回id值 需要配置useGeneratedKeys 和 keyProperty或keyColumn -->
<insert id="insertOne" parameterType="user" useGeneratedKeys="true" keyProperty="id">
insert into user values (#{id}, #{username}, #{password}, #{birthday})
</insert>
<update id="updateOne" parameterType="user">
update user set birthday = #{birthday} where id = #{id}
</update>
<delete id="deleteById" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
编写NewUserMapper映射⽂件 mapper查询,namespace为接口
<?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.lpy.mapper.IUserMapper">
<sql id="selectUser">
select * from user
</sql>
<select id="findAll" resultType="com.lpy.model.User">
<include refid="selectUser"></include>
</select>
<select id="findByCondition" resultType="user" parameterType="user">
<include refid="selectUser"></include>
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="username != null">
and username = #{username}
</if>
<if test="birthday != null">
and birthday = #{birthday}
</if>
</where>
</select>
<select id="findByIds" resultType="user" parameterType="list">
<include refid="selectUser"></include>
<where>
<foreach collection="list" open="id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
</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>
<properties resource="jdbc.properties" />
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<typeAlias type="com.lpy.model.User" alias="user"/>
</typeAliases>
<environments default="dev">
<environment id="dev">
<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 resource="UserMapper.xml"/>
<mapper resource="NewUserMapper.xml"/>
</mappers>
</configuration>
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis1
jdbc.username=root
jdbc.password=root
编写Dao、Mapper
package com.lpy.dao;
import com.lpy.model.User;
import java.io.IOException;
import java.util.List;
/**
* @author lipengyu
*/
public interface IUserDao {
List<User> findAll() throws IOException;
}
编写实现是为了测试传统使用statementId查询
package com.lpy.dao.impl;
import com.lpy.dao.IUserDao;
import com.lpy.model.User;
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 java.io.IOException;
import java.util.List;
/**
* @author lipengyu
*/
public class IUserDaoImpl implements IUserDao {
@Override
public List<User> findAll() throws IOException {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("sqlMapConfig.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession(true);
List<User> users = sqlSession.selectList("userMapper.findAll");
sqlSession.close();
return users;
}
}
mapper则使用动态代理
package com.lpy.mapper;
import com.lpy.model.User;
import java.io.IOException;
import java.util.List;
/**
* @author lipengyu
*/
public interface IUserMapper {
List<User> findAll();
List<User> findByCondition(User user);
List<User> findByIds(List<Integer> ids);
}
写测试类
这里用的是junit4,如果发现@Before不生效,请确认是不是其他版本
import com.lpy.dao.IUserDao;
import com.lpy.dao.impl.IUserDaoImpl;
import com.lpy.mapper.IUserMapper;
import com.lpy.model.User;
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.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
/**
* @author lipengyu
*/
public class MybatisTest {
SqlSession sqlSession = null;
IUserMapper iUserMapper = null;
@Before
public void init() throws IOException {
// 1. Resources工具类,配置文件的加载,把配置文件加载成字节输入流
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2. 解析了配置文件,并创建sqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// 3. 生产sqlSession 默认开启一个事务,但是该事务不会自动提交,在进行增删改操作时,要收动提交事务
// sqlSession = sqlSessionFactory.openSession();
// 当传入true参数 事务自动提交
sqlSession = sqlSessionFactory.openSession(true);
// 4.sqlSession 调用方法
// sqlSession.selectList()...
// 获取mapper对象
iUserMapper = sqlSession.getMapper(IUserMapper.class);
}
@After
public void close() {
sqlSession.close();
}
@Ignore
@Test
public void test() {
System.out.println("1");
}
@Test
public void selectList() {
List<User> users = sqlSession.selectList("userMapper.findAll");
users.forEach(System.out::println);
}
@Test
public void insert() {
User user = new User();
user.setUsername("lpy");
user.setPassword("123");
user.setBirthday("2020-12-03");
sqlSession.insert("userMapper.insertOne", user);
System.out.println(user);
List<User> users = sqlSession.selectList("userMapper.findOneByCondition", user);
users.forEach(System.out::println);
}
@Test
public void update() {
List<User> users = sqlSession.selectList("userMapper.findAll");
Integer id = users.get(0).getId();
User param = new User();
param.setId(id);
param.setBirthday("2020-12-02");
sqlSession.update("userMapper.updateOne", param);
User user = sqlSession.selectOne("userMapper.findById", id);
System.out.println(user);
}
@Test
public void delete() {
List<User> users = sqlSession.selectList("userMapper.findAll");
Integer id = users.get(users.size() - 1).getId();
sqlSession.delete("userMapper.deleteById", id);
User user = sqlSession.selectOne("userMapper.findById", id);
System.out.println(user);
}
@Test
public void traditional() throws IOException {
IUserDao iUserDao = new IUserDaoImpl();
System.out.println(iUserDao.findAll());
}
@Test
public void mapper() {
System.out.println(iUserMapper.findAll());
}
@Test
public void dynamicCondition() {
User param = new User();
param.setId(1);
System.out.println(iUserMapper.findByCondition(param));
User param2 = new User();
param2.setUsername("lucy");
System.out.println(iUserMapper.findByCondition(param2));
User param3 = new User();
param3.setBirthday("2020-12-02");
System.out.println(iUserMapper.findByCondition(param3));
}
@Test
public void foreach() {
System.out.println(iUserMapper.findByIds(Arrays.asList(1,2)));
}
}
mybatis 映射文件概述
Mybatis相应API介绍
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 中加载资源⽂件。
SqlSession⼯⼚对象SqlSessionFactory
SqlSessionFactory 有多个个⽅法创建SqlSession 实例。常⽤的有如下两个:
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层实现
传统开发方式
可以参照上方代码示例,需要实现DAO层,实际就是直接根据statementId匹配xml中的sql
代理开发方式
采⽤ 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的类型相同
三、mybaits 配置文件深入
environments标签
数据库环境的配置,⽀持多环境配置
其中,事务管理器(transactionManager)类型有两种:
- JDBC:这个配置就是直接使⽤了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作
⽤域。 - MANAGED:这个配置⼏乎没做什么。它从来不提交或回滚⼀个连接,⽽是让容器来管理事务的整个⽣
命周期(⽐如 JEE 应⽤服务器的上下⽂)。 默认情况下它会关闭连接,然⽽⼀些容器并不希望这样,因
此需要将 closeConnection 属性设置为 false 来阻⽌它默认的关闭⾏为。
其中,数据源(dataSource)类型有三种:
- UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED:这种数据源的实现利⽤“池”的概念将 JDBC 连接对象组织起来。
- JNDI:这个数据源的实现是为了能在如 EJB 或应⽤服务器这类容器中使⽤,容器可以集中或在外部配
置数据源,然后放置⼀个 JNDI 上下⽂的引⽤。mapper标签
该标签的作⽤是加载映射的,加载⽅式有如下⼏种: ```xml
使⽤相对于类路径的资源引⽤,例如:
使⽤完全限定资源定位符(URL),例如:
使⽤映射器接⼝实现类的完全限定类名,例如:
将包内的映射器接⼝实现全部注册为映射器,例如:
使用package时需要保证类文件和mapper.xml文件同包同名 ``` properties标签
实际开发中,习惯将数据源的配置信息单独抽取成⼀个properties⽂件,该标签可以加载额外配置的properties⽂件
typeAliases标签
类型别名是为Java 类型设置⼀个短的名字。原来的类型名称配置如下
配置typeAliases,为com.lagou.domain.User定义别名为user
上⾯我们是⾃定义的别名, mybatis框架已经为我们设置好的⼀些常⽤的类型的别名
映射配置⽂件mapper.xml
动态sql语句概述
Mybatis 的映射⽂件中,前⾯我们的 SQL 都是⽐较简单的,有些时候业务逻辑复杂时,我们的 SQL是
动态变化的,此时在前⾯的学习中我们的 SQL 就不能满⾜要求了。
参考的官⽅⽂档,描述如下:
SQL⽚段抽取
sql 中可将重复的 sql 提取出来,使⽤时⽤ include 引⽤即可,最终达到 sql 重⽤的⽬的
```sqlselect * from user
<a name="KDXbL"></a>
#### if
我们根据实体类的不同取值,使⽤不同的 SQL语句来进⾏查询。⽐如在 id如果不为空时可以根据id查询,如果username 不同空时还要加⼊⽤户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
java
```java
User param = new User();
param.setId(1);
System.out.println(iUserMapper.findByCondition(param));
User param2 = new User();
param2.setUsername("lucy");
System.out.println(iUserMapper.findByCondition(param2));
User param3 = new User();
param3.setBirthday("2020-12-02");
System.out.println(iUserMapper.findByCondition(param3));
不同参数打印的sql不同
sql
select * from user WHERE id = ?
select * from user WHERE username = ?
select * from user WHERE birthday = ?
#### for
循环执⾏sql的拼接操作,例如: select from user WHERE id in ( ? , ? )
sql
<select id="findByIds" resultType="user" parameterType="list">
<include refid="selectUser"></include>
<where>
<foreach collection="list" open="id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
sql
@Test
public void foreach() {
System.out.println(iUserMapper.findByIds(Arrays.asList(1,2)));
}
foreach标签的属性含义如下:标签⽤于遍历集合,它的属性: - collection:代表要遍历的集合元素,注意编写时不要写#{} - open:代表语句的开始部分 - close:代表结束部分 - item:代表遍历集合的每个元素,⽣成的变量名 - sperator:代表分隔符
# 四、Mybatis复杂映射开发 新建项目mybatis_muiltitable > 这里我直接贴最后的代码了 导入pom.xml文件
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lpy</groupId>
<artifactId>mybatis_muiltitable</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>
<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>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.1.2</version>
</dependency>
</dependencies>
</project>
新建sqlMapConfig.xml
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 resource="jdbc.properties" />
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<typeAlias type="com.lpy.model.User" alias="user"/>
</typeAliases>
<plugins>
<!-- <plugin interceptor="com.lpy.plugin.MyPlugin">-->
<!-- <property name="name" value="Bob"/>-->
<!-- </plugin>-->
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
</plugin>
</plugins>
<environments default="dev">
<environment id="dev">
<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>
<package name="com.lpy.mapper"/>
</mappers>
</configuration>
jdbc.properties
xml
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis1
jdbc.username=root
jdbc.password=root
redis.properties
xml
redis.host=localhost
redis.port=6379
redis.connectionTimeout=5000
redis.password=
redis.database=0
## ⼀对⼀查询
⼀对⼀查询的模型⽤户表和订单表的关系为,⼀个⽤户有多个订单,⼀个订单只从属于⼀个⽤户
⼀对⼀查询的需求:查询⼀个订单,与此同时查询出该订单所属的⽤户 ### 创建order表
java
CREATE TABLE `orders` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`order_time` datetime DEFAULT NULL,
`total` decimal(18,2) DEFAULT NULL,
`uid` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
⼀对⼀查询的sql语句
对应的sql语句: select from order o,user u where o.uid=u.id;
查询的结果如下:
创建Order和User实体 > 注释的部分后面会用到
java
package com.lpy.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;
import java.util.Date;
/**
* @author lipengyu
*/
@Getter
@Setter
@ToString
//@ToString(exclude = "user")
public class Orders implements Serializable {
private Integer id;
private Date orderTime;
private double total;
private String uid;
private User user;
}
java
package com.lpy.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
/**
* @author lipengyu
*/
@Getter
@Setter
@ToString
@Table(name = "user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
private String birthday;
// @Transient
// private List<Orders> orders;
// @Transient
// private List<Role> roles;
}
在resources下的com.lpy.mapper下新建IOrderMapper.xml
### IOrderMapper.xml
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.lpy.mapper.IOrderMapper">
<!--oneToOne1-->
<resultMap id="orderMap1" type="com.lpy.model.Order">
<result column="order_time" property="orderTime"/>
<result column="uid" property="user.id"/>
<result column="username" property="user.username"/>
<result column="password" property="user.password"/>
<result column="birthday" property="user.birthday"/>
</resultMap>
<!--oneToOne2-->
<resultMap id="orderMap2" type="com.lpy.model.Order">
<result column="id" property="id"/>
<result column="order_time" property="orderTime"/>
<result column="total" property="total"/>
<result column="uid" property="uid"/>
<association property="user" javaType="com.lpy.model.User">
<result column="uid" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="birthday" property="birthday"/>
</association>
</resultMap>
<select id="findAll" resultMap="orderMap1">
SELECT * FROM orders o left join user u on o.uid = u.id
</select>
</mapper>
此处注意,因为我们在sqlMapConfig.xml中使用了mappers -> package 的方式,所以需要mapper类和xml文件同包同名。这里展示了两种一对一查询的方式
java
@Test
public void oneToOne() {
iOrderMapper.findAll().forEach(System.out::println);
}
java
Orders(id=1, orderTime=Sat Oct 23 10:01:46 CST 2021, total=5.0, uid=1, user=User(id=1, username=lucy, password=1, birthday=2020-12-02))
Orders(id=2, orderTime=Mon Oct 25 10:01:46 CST 2021, total=10.0, uid=2, user=User(id=2, username=lip, password=2, birthday=2021-10-21))
Orders(id=3, orderTime=Wed Oct 27 10:01:46 CST 2021, total=15.0, uid=3, user=User(id=3, username=sc, password=3, birthday=2021-10-21))
## ⼀对多查询
⼀对多查询的模型同一对一的模型是相同的⽤户表和订单表的关系为,⼀个⽤户有多个订单,⼀个订单只从属于⼀个⽤户
⼀对多查询的需求:查询⼀个⽤户,与此同时查询出该⽤户具有的订单
修改User和Orders实体
java
package com.lpy.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
/**
* @author lipengyu
*/
@Getter
@Setter
@ToString
@Table(name = "user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
private String birthday;
@Transient
private List<Orders> orders;
// @Transient
// private List<Role> roles;
}
java
package com.lpy.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;
import java.util.Date;
/**
* @author lipengyu
*/
@Getter
@Setter
//@ToString
@ToString(exclude = "user")
public class Orders implements Serializable {
private Integer id;
private Date orderTime;
private double total;
private String uid;
private User user;
}
在resources下的com.lpy.mapper下新建IUserMapper.xml
### IUserMapper.xml
```java
<?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">
<resultMap id="userRoleMap" type="com.lpy.model.User">
<result column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="birthday" property="birthday"/>
<collection property="roles" ofType="com.lpy.model.Role">
<result column="rId" property="id"/>
<result column="roleName" property="roleName"/>
</collection>
</resultMap>
<select id="findAllWithRole" resultMap="userRoleMap">
SELECT u.id, u.username, u.password, u.birthday, r.id rId, r.role_name roleName
FROM user u
left join user_role ur on ur.user_id = u.id
left join role r on ur.role_id = r.id
</select>
sql是findAll那一条。<br />与OneToOne不同这里不再是**association**标签 而是**collection**标签
```java
@Test
public void oneToMany() {
System.out.println(iUserMapper.findAll());
}
结果:
User(id=1, username=lucy, password=1, birthday=2020-12-02, orders=[Orders(id=1, orderTime=Sat Oct 23 10:01:46 CST 2021, total=5.0, uid=1)])
User(id=2, username=lpy, password=2, birthday=2021-10-21, orders=[Orders(id=2, orderTime=Mon Oct 25 10:01:46 CST 2021, total=10.0, uid=2)])
User(id=3, username=sc, password=3, birthday=2021-10-21, orders=[Orders(id=3, orderTime=Wed Oct 27 10:01:46 CST 2021, total=15.0, uid=3)])
多对多查询
⽤户表和⻆⾊表的关系为,⼀个⽤户有多个⻆⾊,⼀个⻆⾊被多个⽤户使⽤
多对多查询的需求:查询⽤户同时查询出该⽤户的所有⻆⾊
多对多模型
创建role表
CREATE TABLE `role` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`role_name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
创建user_role表
CREATE TABLE `user_role` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) unsigned DEFAULT NULL,
`role_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
创建Role实体
package com.lpy.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;
/**
* @author lipengyu
*/
@Getter
@Setter
@ToString
public class Role implements Serializable {
private Integer id;
private String roleName;
}
修改User实体
package com.lpy.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
/**
* @author lipengyu
*/
@Getter
@Setter
@ToString
@Table(name = "user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
private String birthday;
// @Transient
// private List<Orders> orders;
@Transient
private List<Role> roles;
}
测试:
/**
* <collection property="orders" ofType="com.lpy.model.Role">
* xml配错偶然发现mybatis可以把A类型的数据设置到B类型的成员变量中
* 比如把Role设置到Orders中
*/
@Test
public void manyToMany() {
iUserMapper.findAllWithRole().forEach(System.out::println);
}
结果:
User(id=1, username=lucy, password=1, birthday=2020-12-02, roles=[Role(id=1, roleName=管理员)])
User(id=2, username=lpy, password=2, birthday=2021-10-21, roles=[Role(id=2, roleName=游客)])
User(id=3, username=sc, password=3, birthday=2021-10-21, roles=[Role(id=3, roleName=超级管理员)])
五、Mybatis注解开发
MyBatis的增删改查
常用注解:
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result ⼀起使⽤,封装多个结果集
@One:实现⼀对⼀结果集封装
@Many:实现⼀对多结果集封装
6.2 MyBatis的增删改查
我们完成简单的user表的增删改查的操作
IRoleMapper.xml
@Insert("insert into role values(#{id}, #{roleName})")
void insert(Role role);
@Select("select id, role_name roleName from role where id = #{id}")
List<Role> findById(Integer id);
@Update("update role set role_name = #{roleName} where id = #{id}")
void update(Role role);
@Delete("delete from role where id = #{id}")
void delete(Integer id);
@Test
public void annotationInsert() {
IRoleMapper iRoleMapper = sqlSession.getMapper(IRoleMapper.class);
Role role = new Role();
role.setId(3);
role.setRoleName("common");
iRoleMapper.insert(role);
}
@Test
public void annotationSelect() {
System.out.println(iRoleMapper.findById(3));
}
@Test
public void annotationUpdate() {
Role role2 = new Role();
role2.setId(3);
role2.setRoleName("common2");
iRoleMapper.update(role2);
}
@Test
public void annotationDelete() {
iRoleMapper.delete(3);
}
因为这里我们使⽤了注解替代的映射⽂件,所以我们只需要加载使⽤了注解的Mapper接⼝即可 ()
<mappers>
<mapper class="com.lpy.mapper.IRoleMapper"/>
</mappers>
当然当引入多个接口的时候会比较麻烦,所以还是采用package的方式会更方便
<mappers>
<package name="com.lpy.mapper"/>
</mappers>
MyBatis的注解实现复杂映射开发
实现复杂关系映射之前我们可以在映射⽂件中通过配置来实现,使⽤注解开发后,我们可以使⽤
@Results注解, @Result注解, @One注解, @Many注解组合完成复杂关系的配置
一对一查询
在IOrderMapper新增方法
@Select("select * from orders")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "order_time", property = "orderTime"),
@Result(column = "total", property = "total"),
@Result(column = "uid", property = "uid"),
@Result(column = "uid", property = "user", javaType = User.class
, one = @One(select = "com.lpy.mapper.IUserMapper.findById", fetchType = FetchType.LAZY))
})
List<Orders> findAll2();
在IUserMapper中新增方法
@Select("select * from user where id = #{id}")
User findById(Integer id);
新增测试方法
@Test
public void annotationOneToOne() {
iOrderMapper.findAll2().forEach(System.out::println);
}
但是此时会执行n + 1 条sql
select * from orders
select * from user where id = ?
select * from user where id = ?
select * from user where id = ?
一对多查询
在IUserMapper中新增方法
@Select("select * from user")
@Results({
@Result(column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "password", property = "password"),
@Result(column = "birthday", property = "birthday"),
@Result(column = "id", property = "orders", javaType = List.class
, many = @Many(select = "com.lpy.mapper.IOrderMapper.findByUserId", fetchType = FetchType.LAZY))
})
List<User> findAll2();
在IOrderMapper新增方法
@Select("select * from orders where uid = #{uid}")
List<Orders> findByUserId(String uid);
新增测试方法
@Test
public void annotationOneToMany() {
iUserMapper.findAll2().forEach(System.out::println);
}
同样也会执行n + 1条sql
select * from user
select * from orders where uid = ?
select * from orders where uid = ?
select * from orders where uid = ?
多对多查询
在IUserMapper中新增方法
@Select("select * from user")
@Results({
@Result(column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "password", property = "password"),
@Result(column = "birthday", property = "birthday"),
@Result(column = "id", property = "roles", javaType = List.class
, many = @Many(select = "com.lpy.mapper.IRoleMapper.findAllByUserId", fetchType = FetchType.LAZY))
})
List<User> findAllWithRole2();
在IRoleMapper中新增方法
@Select("SELECT r.id, r.role_name roleName FROM role r left join user_role ur on r.id = ur.role_id where ur.user_id = #{userId}")
List<Role> findAllByUserId(String userId);
新增测试方法
@Test
public void annotationManyToMany() {
iUserMapper.findAllWithRole2().forEach(System.out::println);
}
执行sql
select * from user
SELECT r.id, r.role_name roleName FROM role r left join user_role ur on r.id = ur.role_id where ur.user_id = ?
SELECT r.id, r.role_name roleName FROM role r left join user_role ur on r.id = ur.role_id where ur.user_id = ?
SELECT r.id, r.role_name roleName FROM role r left join user_role ur on r.id = ur.role_id where ur.user_id = ?