前言
本次案例以简单的用户和账户的模型来分析 Mybatis 多表关系。用户为 t_user 表,账户为t_account 表。一个用户(User)可以有多个账户(Account),但是一个账户(Account)只能属于一个用户(User), 具体关系如下:
创建maven项目
准备工作
- pom.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.itheima</groupId>
<artifactId>day37_mybatis_02</artifactId>
<version>1.0-SNAPSHOT</version>
<!--引入依赖-->
<dependencies>
<!--junit的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--引入lombok的依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!--mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--引入日志打印依赖-->
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.6</version>
</dependency>
<!--log end-->
</dependencies>
</project>
- 在resource中创建jdbc. properties和log4j.properties配置文件
jdbc. properties
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8
jdbc.driver=com.mysql.jdbc.Driver
log4j.properties
log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#[%-5p] %t %l %d %rms:%m%n
#%d{yyyy-MM-dd HH:mm:ss,SSS\} %-5p [%t] {%c}-%m%n
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %t %l %d %rms:%m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=D:\\idea_project\\itheima_mm_backend.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS\} %-5p [%t] {%c}-%m%n
- 创建pojo类
User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private Integer uid;
private String username;
private String sex;
private Date birthday;
private String address;
/**
* 表示User和Account的一对多的关系
*/
private List<Account> accountList;
}
Account
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account implements Serializable {
private Integer aid;
private Double money;
private Integer uid;
/**
* 表示Account和User一对一的关系
*/
private User user;
}
- 编写SqlMapConfig.xml核心配置文件
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>
<!--引入外部jdbc配置信息-->
<properties resource="jdbc.properties"/>
<settings>
<!-- 配置二级缓存(默认开启, 可以不用设置)-->;
<!--<setting name="cacheEnabled" value="true"/>-->
<!--配置全局延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false" />
</settings>
<!--别名设置-->
<typeAliases>
<package name="com.itheima.pojo"/>
</typeAliases>
<!--数据库环境配置-->
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
<property name="driver" value="${jdbc.driver}"/>
</dataSource>
</environment>
</environments>
<!--加载映射配置文件-->
<mappers>
<package name="com.itheima.dao"/>
</mappers>
</configuration>
- 引入工具类SqlSessionFactoryUtils
public class SqlSessionFactoryUtils {
private static InputStream is;
private static SqlSessionFactory sessionFactory;
static {
try {
//1. 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//2. 读取核心配置文件,转换成字节输入流
is = Resources.getResourceAsStream("SqlMapConfig.xml");
//3. 创建SqlSessionFactory对象
sessionFactory = sqlSessionFactoryBuilder.build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession对象的方法
* @return
*/
public static SqlSession openSession(){
SqlSession sqlSession = null;
try {
//4. 创建SqlSession对象
sqlSession = sessionFactory.openSession();
is.close();
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
return sqlSession;
}
/**
* 提交事务并且关闭资源
* @param sqlSession
*/
public static void commitAndClose(SqlSession sqlSession){
sqlSession.commit();
sqlSession.close();
}
/**
* 回滚事务并且关闭资源
* @param sqlSession
*/
public static void rollbackAndClose(SqlSession sqlSession){
sqlSession.rollback();
sqlSession.close();
}
}
编写dao接口
AccountDao
public interface AccountDao {
/**
* 根据aid查询账户信息
* @param aid
* @return
*/
Account findAccountByAid(Integer aid);
/**
* 根据uid查询Account的集合
* @param uid
* @return
*/
List<Account> findAccountListByUid(Integer uid);
}
UserDao
public interface UserDao {
/**
* 根据uid查询用户信息
* @param uid
* @return
*/
User findUserByUid(Integer uid);
/**
* 根据uid进行一对多的分布查询
* @param uid
* @return
*/
User findByUid(Integer uid);
}
编写AccountDao.xml和UserDao.xml映射配置文件
AccountDao.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.itheima.dao.AccountDao">
<!--
使用resultMap标签自定义映射规则
-->
<resultMap id="accountUserMap" type="Account">
<!--对主键进行映射-->
<id column="aid" property="aid"/>
<!--对非主键进行映射-->
<result column="money" property="money"/>
<result column="uid" property="uid"/>
<!--
使用<association>标签进行一对一映射配置
select属性: 表示调用其他的select标签, 需要写权限定名.id
column属性: 表示需要往调用的其他select标签传入的参数
fetchType="lazy"表示延迟加载(局部配置,只有配置了这个的地方才会延迟加载)
property表示要映射的pojo的属性名
javaType表示要进行映射的POJO的属性的类型
-->
<association property="user" javaType="User" column="uid" select="com.itheima.dao.UserDao.findUserByUid"/>
</resultMap>
<select id="findAccountByAid" parameterType="int" resultMap="accountUserMap">
select * from t_account where aid = #{aid}
</select>
<!--根据uid查询账户集合-->
<select id="findAccountListByUid" parameterType="int" resultType="Account">
select * from t_account where uid = #{uid}
</select>
</mapper>
UserDao.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.itheima.dao.UserDao">
<select id="findUserByUid" parameterType="int" resultType="User">
select * from t_user where uid = #{uid}
</select>
<!--一对多分布查询-->
<!--自定义映射规则-->
<resultMap id="userAccountMap" type="User">
<!--对主键进行映射-->
<id column="uid" property="uid"/>
<!--对非主键进行映射-->
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
<!--
使用collection进行一对多映射配置
column属性: 表示要往调用select标签的传入的参数
select属性: 表示可以调用其他的select标签, 需要写全限定名.id
property表示要映射的pojo的属性名
ofType表示要进行映射的POJO的属性的类型
-->
<collection property="accountList" ofType="Account" column="uid" select="com.itheima.dao.AccountDao.findAccountListByUid"/>
</resultMap>
<select id="findByUid" parameterType="int" resultMap="userAccountMap">
select * from t_user where uid = #{uid}
</select>
</mapper>
编写测试类
public class TestMybatis {
@Test
public void testAccount() {
SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
//获取accountDao代理对象
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
//获取userDao代理对象
//UserDao userDao = sqlSession.getMapper(UserDao.class);
Account account = accountDao.findAccountByAid(2);
/*User user = userDao.findUserByUid(account.getUid());
account.setUser(user);*/
System.out.println(account.getMoney());
//System.out.println(account);
//提交事务, 并关闭资源
SqlSessionFactoryUtils.commitAndClose(sqlSession);
}
@Test
public void testUser() {
SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
//获取UserDao的代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
//调用查询方法
User user = userDao.findByUid(2);
//System.out.println(user);
//懒加载, 只会执行一步sql语句查询
System.out.println(user.getAddress());
//提交事务, 并关闭资源
SqlSessionFactoryUtils.commitAndClose(sqlSession);
}
}
总结
- 以哪张表作为主体查询,那么就将查询到的结果封装到哪张表对应的POJO对象中
- 如果表的关系是一对一,那么就在一个POJO中添加另外一个POJO的对象属性
- 如果表的关系是一对多,那么就在一个POJO中添加另外一个POJO的集合属性
- 使用association标签可以进行一对一的映射
- 使用collection标签可以进行一对多的映射
- 其中标签属性:
- column属性: 表示要往调用select标签的传入的参数
select属性: 表示可以调用其他的select标签, 需要写全限定名.id
property属性: 表示要映射的pojo的属性名
javaType属性(一对一)和ofType属性(一对多)表示要进行映射的POJO的属性的类型
END