一、多表操作
前言
多表查询有多少种?
- 1对1
- 1对多(多对一)
- 多对多
环境搭建(可忽略)
1、数据库
2、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>MyBatis03</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--分页插件-->
<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>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
<scope>runtime</scope>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11.0</source>
<target>11.0</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
3、sqlMapCongfig.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"/>
<!--自定义别名-->
<typeAliases>
<typeAlias type="com.itheima.domain.User" alias="user" />
<typeAlias type="com.itheima.domain.Order" alias="order" />
</typeAliases>
<!--注册类型转换器-->
<typeHandlers>
<typeHandler handler="com.itheima.handler.DateTpyeHandler"/>
</typeHandlers>
<!--配置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/> <!--指定方言:(指定书目数据库)因为不同数据库sql命令可能不一样-->
</plugin>
</plugins>
<!--搭建数据源环境-->
<environments default="develoment">
<environment id="develoment">
<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="com/itheima/mapper/UserMapper.xml"/>
<mapper resource="com/itheima/mapper/OrderMapper.xml"/>
</mappers>
</configuration>
关于自定义别名:
【还有1中自定义别名的方式是:扫包】
<!--自定义别名-->
<typeAliases>
<package name = "com.iiheima.domain"/>
【扫实体类的包,它会将包下的所有实体类都定义别名,别名该类名的首字母小写。】
4、实体类:User&Order(订单)
public class User {
private int id;
private String username;
private String password;
//忽略get、set方法和toString
}
public class Order {
private int id;
private Date orderTime;//下单时间
private double total;//订单价格
private User user; //当前订单属于哪个客户
//忽略get、set方法和toString
5、UserMapper&OrderMapper:空
6、Date转毫秒值的类型转换器(省略)
代码演示多表操作
1)一对一
1个订单对应1个User
期望: 查询Order表,返回所有Order对象List集合,并且能够显示Order从属的User信息
问题: 单单查询Order表是不能够查询出来User的,因为Order表中没有User类型的字段,只有1个相关的uid。
铺垫:
先让order表和user表结合成1个表:内连接查询
SELECT * , o.id oid FROM orders o , USER u WHERE u.id = o.id
解决:
手动指定:字段名与实体类属性的映射关系
(将查询结果集的字段 映射到 实体类的属性中)
①OrderMapper接口
public interface OrderMapper {
List<Order> findAll();
}
②OrderMapper.xml
<mapper namespace="com.itheima.mapper.OrderMapper">
<!--
【手动指定:字段名与实体类属性的映射关系 (将查询结果集的字段映射到实体类的属性中)】
type:你要封进的实体类
column:数据库字段名称 (id:表示 主键 result:表示 普通字段 【都是返回查询结果的字段】)
property:实体类属性
-->
<resultMap id="orderMap" type="order"> <!--order自定义别名(全限定名)-->
//Order的简单属性
<id column="oid" property="id"/> <!--数据库的oid 对应着 实体类属性的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"/>
</resultMap>
<select id="findAll" resultMap="orderMap"> <!--resultMa:指定哪个结果映射的id标识-->
SELECT * , o.id oid FROM orders o , USER u WHERE u.id = o.uid
</select>
</mapper>
③测试
@Test
public void test01() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取动态代理对象
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
//执行SQL
List<Order> orders = mapper.findAll();
for (Order order : orders) {
System.out.println(order);
}
sqlSession.close();
}
输出:
Order{id=1, orderTime=Thu Jan 01 08:00:02 CST 1970, total=3000.0, user=User{id=1, username='lucy', password='123', birthday=Thu Jan 01 08:00:02 CST 1970}}
Order{id=2, orderTime=Thu Jan 01 08:00:02 CST 1970, total=4000.0, user=User{id=1, username='lucy', password='123', birthday=Thu Jan 01 08:00:02 CST 1970}}
Order{id=3, orderTime=Thu Jan 01 08:00:02 CST 1970, total=5000.0, user=User{id=2, username='haohao', password='123', birthday=Thu Jan 01 08:00:02 CST 1970}}
映射过程
映射原理
Mapper的第二种配置:
OrderMapper.xml:
<resultMap id="orderMap" type="order"> <!--自定义别名-->
//Order的简单属性
<id column="oid" property="id"/> <!--数据库的oid 对应着 实体类属性的id-->
<result column="orderTime" property="orderTime"/>
<result column="total" property="total"/>
//Order的User集合属性
<!--
association :翻译“匹配”
property:表示实体类(order)的属性名称(private User user)
javaType:表示实体类属性的类型(User)(全限定名)
column:数据库返回结果集的字段名称
property:实体类属性名称
-->
<association property="user" javaType="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="birthday" property="birthday"/>
</association>
</resultMap>
2)一对多
1个User对应多个订单
期望: 查询用户,显示用户的所有信息和有哪些订单
问题:
1)单单查询User表显示不了order表的信息,但发现order关联user的id。
2)mapper怎么手动指定;List集合属性的映射关系
解决:
铺垫:
先让order表和user表结合成1个表:内连接查询
SELECT * , o.id oid FROM orders o , USER u WHERE u.id = o.id
①User添加属性
public class User {
private int id;
private String username;
private String password;
private Date birthday;
//当前用户有哪些订单
private List<Order> orderList;
②UserMapper接口
public interface UserMapper {
List<User> findAll();
}
②UserMapper.xml
<mapper namespace="com.itheima.mapper.UserMapper">
<resultMap id="userMap" type="user">
//User的简单属性
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="birthday" property="birthday"/>
//User的Order集合属性
<!--配置集合:
property:User中集合的属性名称(List<Order> orderList)
ofType:集合的类型(Order)(全限定名)
column:字段名称
-->
<collection property="orderList" ofType="order">
<!--封装Order数据-->
<id column="oid" property="id"/>
<result column="ordertime" property="orderTime"/>
<result column="total" property="total"/>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
select * ,o.id oid from user u ,orders o where u.id=o.uid
</select>
</mapper>
③测试
@Test
public void test02() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//执行SQL
List<User> users = mapper.findAll();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
输出:
User{id=1, username='lucy', password='123', birthday=Thu Jan 01 08:00:02 CST 1970, orderList=[Order{id=1, orderTime=Thu Jan 01 08:00:02 CST 1970, total=3000.0, user=null}, Order{id=2, orderTime=Thu Jan 01 08:00:02 CST 1970, total=4000.0, user=null}]}
User{id=2, username='haohao', password='123', birthday=Thu Jan 01 08:00:02 CST 1970, orderList=[Order{id=3, orderTime=Thu Jan 01 08:00:02 CST 1970, total=5000.0, user=null}]}
3)多对多
1个用户对应多个角色,1个角色对应多个用户
(将3个表进行内连接查询)
0、数据库:
select * from sys_user u,sys_user_role ur ,sys_role r where u.id = ur.userId and r.id = ur.roleId
①增加role实体类
public class Role {
private int id;
private String roleName;
private String roleDesc;
//省略set/get方法
}
②增加user实体类成员变量
public class User {
private int id;
private String username;
private String password;
private Date birthday;
private List<Order> orderList;//当前用户有哪些订单
//当前用户具备哪些角色
private List<Role> roleList;
③UserMapper接口
public interface UserMapper {
List<User> findUserAndRoleAll();
}
④UserMapper.xml
<mapper namespace="com.itheima.mapper.UserMapper">
<resultMap id="userRoleMap" type="user"> <!--type:封进哪里的全限定名-->
<!--User的简单属性-->
<id column="userId" property="id"/> <!--column:结果集字段-->
<result column="username" property="username"/>
<result column="password" property="password"/>
<!--User的roleList的Role集合属性-->
<collection property="roleList" ofType="role"> <!--ofType:集合类型(全限定名)-->
<id column="roleId" property="id"/>
<result column="roleName" property="roleName"/>
<result column="roleDesc" property="roleDesc"/>
</collection>
</resultMap>
<select id="findUserAndRoleAll" resultMap="userRoleMap">
select * from sys_user u ,sys_user_role ur,sys_role r where u.id = ur.userId and r.id = ur.roleId
</select>
</mapper>
⑤测试
@Test
public void test03() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//执行SQL
List<User> users = mapper.findUserAndRoleAll();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
输出:
User{id=1, username='zhangsan', password='123', birthday=null, orderList=null, roleList=[Role{id=1, roleName='院长', roleDesc='负责全面工作'}, Role{id=2, roleName='研究员', roleDesc='课程研发工作'}]}
User{id=2, username='lisi', password='123', birthday=null, orderList=null, roleList=[Role{id=2, roleName='研究员', roleDesc='课程研发工作'}, Role{id=3, roleName='讲师', roleDesc='授课工作'}]}
4)总结
二、注解开发
1)概述
2)简单de增删改查
①Mybatis核心文件配置
删除了mapper配置文件,接口和实体类是没有映射关系的,因为没有配置值文件Mybatis不知道谁跟谁有映射关系,不使用xml配置的话,记得要加载映射关系
【这里是扫接口的包】
<!--加载映射关系-->
<mappers>
<!--指定接口所在的包(扫包)-->
//<!--<mapper resource="com/itheima/mapper/AccountMapper.xml"></mapper>-->
<package name="com.itheima.mapper"/>
</mappers>
②UserMapper接口:声明注解
这里跟xml文件一样的,用方法的参数来赋SQL值
public interface UserMapper {
@Insert("insert into user values(#{id},#{username},#{password})")
void save(User user);
@Update("update user set username=#{username},password=#{password} where id=#{id}")
void update(User user);
@Delete("delete from user where id = #{id}")
void delete(int id);
@Select("select * from user where id = #{id}")
User findById(int id);
@Select("select * from user")
List<User> findAll();
}
③测试
public class Demo {
public User user = new User();
public 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.setUsername("我是谁");
user.setPassword("123");
mapper.save(user);
}
//删
@Test
public void testDeleteById(){
mapper.delete(3);
}
//改
@Test
public void testUpdateById(){
user.setId(1);
user.setUsername("易烊");
user.setPassword("123");
mapper.update(user);
}
//查:1个
@Test
public void testFindAll(){
List<User> userList = mapper.findAll();
System.out.println(userList);
}
//查:所有
@Test
public void testFindById(){
User user = mapper.findById(1);
System.out.println(user);
}
}
3)实现复杂映射开发
下面的案例都是根据在XML配置的案例上,使用注解代替xml
请看上面的 数据库样子 和 案列需求
概述
1、一对一
需求:查询订单,返回订单的从属用户
0、实体类
public class User {
private int id;
private String username;
private String password;
//忽略get、set方法和toString
}
public class Order {
private int id;
private Date orderTime;//下单时间
private double total;//订单价格
private User user; //当前订单属于哪个客户
//忽略get、set方法和toString
①OrderMapper接口
public interface OrderMapper {
@Select("select * from user u,orders o where o.uid = u.id")
@Results({
//Order的简单属性
@Result(column = "id",property = "id"),//返回结果集的字段 与 Order属性值
@Result(column = "ordertime",property = "ordertime"),
@Result(column = "total",property = "total"),
//Order的User对象属性
@Result(column = "uid",property = "user.id"),//返回结果集的字段 与 Order中User对象的属性值
@Result(column = "username",property = "user.username"),
@Result(column = "password",property = "user.password")
})
List<Order> findAll();
}
②测试
public class Demo2 {
public User user = new User();
public 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 test1(){
List<Order> orderList = mapper.findAll();
for (Order order : orderList) {
System.out.println(order);
}
}
③输出
Order{id=1, ordertime='2018-12-12', total='3000', user=User{id=1, username='易烊', password='123'}}
Order{id=1, ordertime='2018-12-12', total='4000', user=User{id=1, username='易烊', password='123'}}
Order{id=2, ordertime='2018-12-12', total='5000', user=User{id=2, username='haohao', password='123'}}
对比一下xml:
2、一对一的第2种方式(常用)
第2种是什么方式呢?
上边的方式是使用
内连接
将2个表合成为1个表来查询
下面是通过1个表,1个表的查询
。
具体是 怎么样呢?
- 先查询orders表返回查询结果集
- 指定查询结果集中的uid(oders表中的外键,user表的主键), 根据uid查询user表
select * from useru,orders o where o.uid = u.id
↓分开
select * from orders (OrderMapper)
↓根据uid(复用)
select * from user where id = ? (UserMapper)
代码跟前面整个注解演示相关,这里只写出有改动的。
①UserMapper接口(上边已经定义<复用>)
根据id查询User:
@Select("select * from user where id = #{id}")
User findById(int id);
②OrderMapper接口
public interface OrderMapper {
@Select("select * from orders")
@Results({
//Order的简单属性
@Result(column = "id",property = "id"),//返回结果集的字段 与 Order属性值
@Result(column = "ordertime",property = "ordertime"),
@Result(column = "total",property = "total"),
//Order的User对象属性
@Result(
property = "user", //Order实体需要封装的属性名称
column = "uid", //根据orders返回查询结果集中的哪个字段进行查询
javaType = User.class, //Order中user属性名称的属性类型
//1对1
one = @One(select = "com.itheima.mapper.UserMapper.findById" ) //select属性:查询哪个接口的方法 获得 Order实体需要封装的属性
)
})//记得,这里是{ }是数组。
List<Order> findAll();
}
3、一对多
需求:查询用户,返回该用户有哪些订单
这里也是将2个表使用
内连接
合成1个表查询,分成2个表分别查询
select* from useru ,orders o where u.id = o.uid
↓分开
select * from user
↓ 根据id
select * from orders where uid = ?
①User实体类
public class User {
private int id;
private String username;
private String password;
//当前用户拥有多少订单
private List<Order> orderList;
②OrderMapper接口
public interface OrderMapper {
@Select("select * from orders where id = #{id}")
List<Order> findByUid(int uid);
③UserMapper接口
public interface UserMapper {
@Select("select * from user")
@Results({
//User的简单属性
@Result(id = true,column = "id",property = "id"), //id=true :特指这个是主键
@Result(column = "username",property = "username"),
@Result(column = "password",property = "password"),
//User中Order集合属性
@Result(
property = "orderList", //User中Order集合属性名称
column = "id", //User返回查询结果集中,根据哪个字段查询
javaType = List.class, //User中orderList属性名称的属性类型
//1对多
many = @Many (select = "com.itheima.mapper.OrderMapper.findByUid") //调用什么方法来查询
)
})
List<User> findUserAndOrderAll();
}
④测试
public class Demo3 {
public User user = new User();
public 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 test1() {
List<User> userList = mapper.findUserAndOrderAll();
for (User u : userList) {
System.out.println(u);
}
}
}
4、多对多
多对多:多对多都是有个中间表,这里也是将3个表的
内连接
,拆分成2个表来查询根据User的id 查询 某个User具备哪些角色:
select * from user u ,sys_user_role ur , sys_role r where ur.roleId = r.id and u.id = ?
↓ 拆分
select * from user
↓ 根据id查询
select * from sys_user_role ur ,sys_role r where ur.roleId = r.id and ur.userId = ?
①Role实体类
public class Role {
private int id;
private String roleName;
private String roleDesc;
②User实体类
public class User {
private int id;
private String username;
private String password;
//当前User具备多少个角色
private List<Role> roleList;
③RoleMapper接口
public interface RoleMapper {
@Select("select * from sys_user_role ur ,sys_role r where ur.roleId = r.id and ur.userId = #{uid}")
List<Role> findByUid(int uid);
}
④UserMapper接口
public interface UserMapper {
@Select("select * from user")
@Results({
//User的简单属性
@Result(column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "password",property = "password"),
//User中Role集合属性
@Result(
property = "roleList", //封进哪个属性
column = "id", //根据哪个字段查询
javaType = List.class, //封进哪个属性的属性类型
//多对多
many = @Many(select = "com.itheima.mapper.RoleMapper.findByUid") //调用某个方法查询
)
})
List<User> findUserAndRoleAll();