文章目录
- 1. 一对一
- 什么是一对一
- User、Order类及Mapper,User、Order表
- 一对一操作的实现
- 一对一操作实现的第二种方式
- 2. 一对多
- 什么是一对多
- 一对多操作实现
- 3. 多对多
- 什么是多对多
- 多对多的实现
- 4. 小结
1. 一对一
什么是一对一
一对一指的是表与表之间通过外键进行连接,比如我们有一个Order类,里面有属性id
、orderTime
、total
,以及一个下这个订单的用户userId
,通过userId
找到对应的用户。一个订单会有一个对应的下单用户,这就是一对一。
User、Order类及Mapper,User、Order表
User表中有如下属性:
Java代码中,
User类的定义如下:
package com.example.demo.domain;
import java.util.Date;
public class User {
int id;
String username;
String password;
Date birthday;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday=" + 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;
}
}
并写好对应的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="com.example.demo.dao.UserMapper">
</mapper>
UserMapper.java
package com.example.demo.dao;
import com.example.demo.domain.User;
import java.io.IOException;
import java.util.List;
public interface UserMapper {
void save(User user);
User findById(int id);
List<User> findAll();
}
Order表中有如下属性:
Java代码中,
Order类的定义如下:
package com.example.demo.domain;
import java.util.Date;
public class Order {
int id;
Date orderTime;
double total;
User user;
@Override
public String toString() {
return "Order{" +
"id=" + id +
", orderTime=" + orderTime +
", total=" + total +
", 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;
}
}
并写好对应的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="com.example.demo.dao.OrderMapper">
</mapper>
OrderMapper.java:
package com.example.demo.dao;
public interface OrderMapper {
}
还有SqlMapConfig.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 通过properties标签加载外部properties文件 -->
<properties resource="jdbc.properties"></properties>
<!-- 自定义别名 -->
<typeAliases>
<typeAlias type="com.example.demo.domain.User" alias="user"></typeAlias>
</typeAliases>
<!-- 配置分页助手插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
<!-- 数据源环境 -->
<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="mapper/UserMapper.xml"></mapper>
<mapper resource="mapper/OrderMapper.xml"></mapper>
</mappers>
</configuration>
一对一操作的实现
假如我们有这样一条sql语句:
SELECT *, o.id `orderid` FROM `order` o, `user` u WHERE o.uid=u.id
两张表中分别有如下数据:
则查询的结果为:
若想要把这条语句中各个属性封装到Order中对应的各个属性中(其中User相关的属性封装到User里),应如下写sql语句:
在orderMapper.java中:
package com.example.demo.dao;
import com.example.demo.domain.Order;
import java.util.List;
public interface OrderMapper {
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="com.example.demo.dao.OrderMapper">
<resultMap id="orderMap" type="com.example.demo.domain.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 `orderid`, u.id `userid` FROM `order` o, `user` u WHERE o.uid=u.id
</select>
</mapper>
其中,我们将SQL语句的返回值通过resultMap
属性指定为orderMap
,并在上面定义了一个id为orderMap
的resultMap
在resultMap
中,type
属性表示返回的具体的类,这里返回的是Order
类,因为我们要将返回的值都封装在Order
类中
下面的<id>
标签表示主键,<result>
表示其他结果
column
用于指定返回的值名称,property
用于指定将返回的值注入到Order
对象的哪个属性里去
其中要注意的事,因为uid,password,birthday
都是要注入到user
这个对象里的,所以在property
中写的是user
下的属性
测试代码:
package com.example.demo;
import com.example.demo.dao.OrderMapper;
import com.example.demo.domain.Order;
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.io.InputStream;
import java.util.List;
public class DemoApplication {
public static void main(String[] args) throws IOException {
// 1. 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 2. 加载SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 3. 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 4. 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
List<Order> orderList = orderMapper.findAll();
for (Order order: orderList){
System.out.println(order);
}
// 6. 释放资源
sqlSession.close();
}
}
查询结果:
一对一操作实现的第二种方式
id为orderMap
的resultMap
还可以用以下的方式写:
association
标签中的property
用于指定属性的名称,javaType
用于指定属性类型,如果我们没有在sqlMapConfig.xml
中配置别名的话,这里就要写com.example.demo.domain.User
下面的内容是类似的,不过通过这个方法就不需要再写user.xxx
了,直接写对应的属性名称就好
<resultMap id="orderMap" type="com.example.demo.domain.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>
运行结果保持不变
2. 一对多
什么是一对多
一对多指的是,比如我们有一个Order类,里面有属性id
、orderTime
、total
,以及一个下这个订单的用户userId
,每个用户可以下多个订单,那就是一对多的关系。
一对多操作实现
想要查询一个用户及其名下的所有订单,我们的SQL语句是这么写的:
select *, o.id oid from `user` u, `order` o where u.id=o.uid
想要查询这种一对多关系的东西,我们应该如下实现:
修改User.java,其中多了一个用户订单的List属性
package com.example.demo.domain;
import java.util.Date;
import java.util.List;
public class User {
int id;
String username;
String password;
Date birthday;
// 描述当前用户存在哪些订单
List<Order> orderList;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday=" + birthday +
", orderList=" + 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;
}
}
UserMapper.java
package com.example.demo.dao;
import com.example.demo.domain.User;
import java.util.List;
public interface UserMapper {
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="com.example.demo.dao.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>
<collection property="orderList" ofType="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, `order` o where u.id=o.uid
</select>
</mapper>
在总配置文件中加上别名配置:
<!-- 自定义别名 -->
<typeAliases>
<typeAlias type="com.example.demo.domain.User" alias="user"></typeAlias>
<typeAlias type="com.example.demo.domain.Order" alias="order"></typeAlias>
</typeAliases>
其中<collection>
表示列表集合,其中property
指的是User
类的属性名称,ofType
是List里面的属性
下面的id
和column
和之前的意思一样
测试代码:
package com.example.demo;
import com.example.demo.dao.UserMapper;
import com.example.demo.domain.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.io.InputStream;
import java.util.List;
public class DemoApplication {
public static void main(String[] args) throws IOException {
// 1. 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 2. 加载SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 3. 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 4. 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.findAll();
for (User user: userList){
System.out.println(user);
}
// 6. 释放资源
sqlSession.close();
}
}
结果如下:
3. 多对多
什么是多对多
其中user_role表中的信息如下:
role表的信息如下:
通过一个user_role表来连接两个表,我们需要通过user_role为媒介进行role具体信息的查询
注意在这里,一个用户可以有多个角色,在user_role
表中两个键均为主键。
一个用户可以有多个角色,一个角色可以被多个用户拥有,就是多对多的关系
多对多的实现
定义Role.java
package com.example.demo.domain;
public class Role {
int id;
String roleName;
String description;
@Override
public String toString() {
return "Role{" +
"id=" + id +
", roleName='" + roleName + '\'' +
", description='" + description + '\'' +
'}';
}
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
在User中加上对应的属性及方法:
package com.example.demo.domain;
import java.util.Date;
import java.util.List;
public class User {
int id;
String username;
String password;
Date birthday;
// 描述当前用户存在哪些订单
List<Order> orderList;
// 描述当前用户的角色
List<Role> roles;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday=" + birthday +
", roles=" + roles +
'}';
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
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;
}
}
在UserMapper.java中加上对应方法的定义:
package com.example.demo.dao;
import com.example.demo.domain.User;
import java.util.List;
public interface UserMapper {
List<User> findAll();
List<User> findUserandRole();
}
在写对应的UserMapper.xml之前,先把SQL语句写好,这实际上就是通过user_role表来查询user对应的role相关的信息:
SELECT * FROM user u, user_role ur, role r where u.id = ur.userid and ur.roleid = r.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="com.example.demo.dao.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>
<collection property="orderList" ofType="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, `order` o where u.id=o.uid
</select>
<resultMap id="userandRoleMap" 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>
<collection property="roles" ofType="role">
<id column="roleid" property="id"></id>
<result column="roleName" property="roleName"></result>
<result column="description" property="description"></result>
</collection>
</resultMap>
<select id="findUserandRole" resultMap="userandRoleMap">
SELECT * FROM user u, user_role ur, role r where u.id = ur.userid and ur.roleid = r.id
</select>
</mapper>
这里对应的语句是findUserandRole
中的语句,返回的Map为userandRoleMap
,里面定义了对应的user信息及role的信息
在总配置中加上别名配置:
<!-- 自定义别名 -->
<typeAliases>
<typeAlias type="com.example.demo.domain.User" alias="user"></typeAlias>
<typeAlias type="com.example.demo.domain.Order" alias="order"></typeAlias>
<typeAlias type="com.example.demo.domain.Role" alias="role"></typeAlias>
</typeAliases>
测试代码:
package com.example.demo;
import com.example.demo.dao.UserMapper;
import com.example.demo.domain.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.io.InputStream;
import java.util.List;
public class DemoApplication {
public static void main(String[] args) throws IOException {
// 1. 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 2. 加载SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 3. 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 4. 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.findUserandRole();
for (User user : userList) {
System.out.println(user);
}
// 6. 释放资源
sqlSession.close();
}
}
结果如下:
和表中查询出来的结果一致
4. 小结
感觉就是如果是一个类里面包含有另一个类(单个对象),就通过<resultMap>
+<association>
就可以
然后对于一个类里面包含有另一个类的列表的,通过<resultMap>
+<collection>
实现即可。
其中<resultMap>
中有属性id
和type
,前者用于指定这个resultMap的名称,type
指定这个resultMap的返回类型
下面的<id>
是主键,<result>
是其他信息,其中包含<column>
是数据库查表得到的列名称,<property>
是具体的属性名
<association>
中的property
是对应的属性名,javaType
是对应的类型
<collection>
中的property
是对应的属性名,ofType
是列表对应的类型