目录
关联查询
当访问关系的一方时,如果需要查看与之关联的另一方数据,则必须使用表链接查询,将查询到的另一方数据,保存在本方的属性中
实体间的关系(拥有 has、属于 belong)
OneToOne:一对一关系(account ←→ user)
OneToMany:一对多关系(user ←→ account)
ManyToMany:多对多关系(user ←→ role)
关联查询的语法
- 指定“一方”关系时(对象),使用
< association javaType="" >
- 指定“多方”关系时(集合),使用
< collection ofType="" >
查询账户(account)信息,关联查询用户(user)信息。
分析:因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。
Account.java
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
// 另外一方
private User user;
//get set toString方法此处省略
}
User.java
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//get set toString方法此处省略
}
public interface AccountMapper {
Account getAccountById(Integer 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="com.by.mapper.AccountMapper">
<resultMap id="getAccountByIdResult" type="com.by.pojo.Account">
<id column="aid" property="id"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
<!--
一对一使用association标签指定数据的封装规则
property="user":Account的属性名
javaType="com.by.pojo.User":等价于resultType
-->
<association property="user" javaType="com.by.pojo.User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
</association>
</resultMap>
<select id="getAccountById" parameterType="int" resultMap="getAccountByIdResult">
SELECT a.id aid, a.uid uid, a.money money, u.* FROM account a LEFT JOIN user u ON a.uid=u.id WHERE a.id=#{id}
</select>
</mapper>
/**
* 一对一:一个Account 对 一个User
*/
@Test
public void testGetAccountById(){
//加载mybatis-config.xml
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//创建sqlSession
SqlSession sqlSession = sessionFactory.openSession();
AccountMapper accountMapper = sqlSession.getMapper(AccountMapper.class);
Account account = accountMapper.getAccountById(1);
System.out.println(account);
//释放资源
sqlSession.close();
inputStream.close();
}
查询所有用户(user)信息及用户关联的账户(account)信息。
分析:用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,此时左外连接查询比较合适。
User.java
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//对应多方,需要用集合
private List<Account> accountList;
//get set toString方法此处省略
}
Account.java
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
// 另外一方
private User user;
//get set toString方法此处省略
}
public interface UserMapper {
User getUserById(Integer 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="com.by.mapper.UserMapper">
<resultMap id="getUserByIdResult" type="com.by.pojo.User">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!--一堆多使用collection标签指定数据的封装规则-->
<collection property="accountList" ofType="com.by.pojo.Account">
<id column="aid" property="id"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
</collection>
</resultMap>
<select id="getUserById" parameterType="int" resultMap="getUserByIdResult">
SELECT u.*, a.id aid, a.uid uid, a.money money FROM user u LEFT JOIN account a ON u.id=a.uid WHERE u.id=#{id}
</select>
</mapper>
/**
* 一对多:一个user 对 多个Account
*/
@Test
public void testGetUserById(){
//加载mybatis-config.xml
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//创建sqlSession
SqlSession sqlSession = sessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(41);
System.out.println(user);
//释放资源
sqlSession.close();
inputStream.close();
}
查询角色及角色赋予的用户信息。
分析:一个用户可以拥有多个角色,一个角色也可以赋予多个用户,用户和角色为双向的一对多关系,多对多关系其实我们看成是双向的一对多关系。
Role.java
public class Role {
private Integer id;
private String roleName;
private String roleDesc;
//对应多方,需要用集合
private List<User> userList;
//get set toString方法此处省略
}
User.java
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//get set toString方法此处省略
}
public interface RoleMapper {
Role getRoleById(Integer 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="com.by.mapper.RoleMapper">
<resultMap id="getRoleByIdResultMap" type="com.by.pojo.Role">
<id column="rid" property="id"></id>
<result column="role_name" property="roleName"></result>
<result column="role_desc" property="roleDesc"></result>
<!--
一对多使用collection标签指定数据的封装规则
property="userList":pojo的属性
ofType="com.by.pojo.User":集合的泛型,等价于resultType
-->
<collection property="userList" ofType="com.by.pojo.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>
<select id="getRoleById" parameterType="int" resultMap="getRoleByIdResultMap">
SELECT
r.id as rid,r.role_name,r.role_desc,
u.*
FROM
user_role ur
JOIN role r ON ur.rid=r.id
JOIN user u ON ur.uid=u.id
WHERE
r.id=#{id}
</select>
</mapper>
/**
* 多对多:一个user 对 多个role 一个role 对 多个user
*/
@Test
public void testGetUserById(){
//加载mybatis-config.xml
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//创建sqlSession
SqlSession sqlSession = sessionFactory.openSession();
@Test
public void testGetRoleById(){
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRoleById(1);
System.out.println(role);
}
//释放资源
sqlSession.close();
inputStream.close();
}
@Insert: 实现新增
@Update:实现更新
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果的集合(其内使用@Result处理当前对象的基本属性,再处理返回值)
@ResultMap:实现引用@Results定义的封装
@One:实现一对一结果集 封装
@Many:实现一对多结果集封装
@SelectProvider:实现动态SQL映射
@CacheNamespace:实现注解二级缓存的使用
注意:复杂的注解不好编写的情况下可以使用Mapper文件配合使用
相关注解介绍 :
@Results注解:
@Results代替了标签< resultMap> 该注解中可以使用单个@Result注解,也可以使用@Result集合格式:
@Results ({@Result () , @Result ()}) 或者@Result (@Result())
@Result 注解:
@Result注解代替了< id> 标签和< result>标签@Result 中 的 属性介绍:
id 是否是主键字段column 数据库中列的名
property 需要装配的属性名
one 需要使用 @One 注解 (@Result (one = @One) ())
many 需要使用的@Many 注解 (@Result (many = @many) ())
@One 注解(一对一):
代替了< association>标签,是多表查询的关键,在注解中用来制定子查询返回单一对象@One 注解属性介绍:
select 指定用来多表查询 的 sqlmapperfetchType 会覆盖全局的配置参数 lazyLoadingEnabled。
语法格式:
@Result(column = " “,property=” ",one=@Onet(select = " "))
@Many 注解(多对一)
代替了< Collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。注意:聚集元素用来处理"一对多"的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList)但是注解中可以不定义;
语法格式:
@Result(property=" “,column=” “,many=@Many(select=” "))
在HusbandMapper.java接口中使用注解进行查询注入数据
HusbandMapper.java
package com.etime.mapper;
import com.etime.pojo.Husband;
import com.etime.pojo.Wife;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface HusbandMapper {
//使用一对一关系注解实现查询
@Select("select * from husband")//查询所有丈夫的信息
//将信息放入结果集合中
@Results({
@Result(property = "hid",column = "hid"),//设置字段属性对应的数据库列名
@Result(property = "hname",column = "hname"),
@Result(property = "wid",column = "wid"),
//对根据丈夫表内的对应妻子wid到妻子表中查找妻子的信息
@Result(property = "wife",javaType = Wife.class,column = "wid",
one=@One(select = "com.etime.mapper.WifeMapper.getWifeByWid"))
})
List<Husband> getAllHusband();
}
在WifeMapper.java中进行注解的使用,对妻子的基本数据进行数据查取
package com.etime.mapper;
import com.etime.pojo.Wife;
import org.apache.ibatis.annotations.Select;
public interface WifeMapper {
//通过@Select查询注解的方方式获取到妻子的基本数据
@Select("select * from wife where wid=#{wid}")
Wife getWifeByWid(int wid);
}
对妻子和丈夫的一对一的关系进行测试
//使用一对一关系注解查询丈夫和妻子的基本信息
@Test
public void t07(){
SqlSession sqlSession =sqlSessionUtil.getSqlSession();
HusbandMapper husbandMapper = sqlSession.getMapper(HusbandMapper.class);
List<Husband> list = husbandMapper.getAllHusband();
list.forEach(System.out::println);
sqlSession.close();
}
这里即将的测试的关系是多个学生属于同一班的关系
创建学生实体类
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Student {
private int sid;
private String sname;
private int cid;
//创建一个班级类的属性给到学生实体类,不同的学生来自不同的班,多个学生属于同一个班级
private Classes classes;
}
创建班级实体类
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Classes {
private int cid;
private String cname;
}
在StudentMapper.java接口中使用注解的方式处理查询语句一查查询到的数据 问题,并处理 查询到的班级classes返回的值
package com.etime.mapper;
import com.etime.pojo.Classes;
import com.etime.pojo.Student;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface StudentMapper {
//使用@Select查询所有学生信息
@Select("select * from student")
//使用@Results注解处理查询到的学生实体类中的基本属性以及对应的班级信息
//并通过one = @One 的方式把查询到接收到的返回班级实体类进行处理
@Results({
@Result(property = "sid",column = "sid"),
@Result(property = "sname",column = "sname"),
@Result(property = "cid",column = "cid"),
@Result(property = "classes",javaType = Classes.class,column = "cid",
one = @One(select = "com.etime.mapper.ClassesMapper.getClassesByCid"))
})
List<Student> getAllStudent();
}
在ClassesMapper.java中使用注解的方式根据班级id查询班级的相关信息
ClassesMapper.java
package com.etime.mapper;
import com.etime.pojo.Classes;
import org.apache.ibatis.annotations.Select;
public interface ClassesMapper {
//使用@Select注解的方式通过查询返回值为班级对象给到学生实体类做数据处理的StudentMapper接口的one = @One处理
@Select("select * from classes where cid=#{cid}")
Classes getClassesByCid(int cid);
}
编写测试
@Test
public void t08(){
SqlSession sqlSession = sqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = studentMapper.getAllStudent();
list.forEach(System.out::println);
sqlSession.close();
}
由上面的准备我们将测试多对一查询的情况
对代码进行修改测试查看是否书写更简便些
修改学生实体类
Student.java
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Student {
private int sid;
private String sname;
private int cid;
}
对班级实体类进行修改
Classes.java
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Classes {
private int cid;
private String cname;
List<Student> stuList;
}
对学生类的数据获取接口做修改
public interface StudentMapper {
//使用@Select查询所有学生信息
@Select("select * from student where cid=#{cid}")
Student getStudentByCid(int cid);
}
对班级类进行数据获取进行数据处理
package com.etime.mapper;
import com.etime.pojo.Classes;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface ClassesMapper {
//使用@Select注解的方式通过查询返回值为学生对象给到班级实体类做数据处理的ClassesMapper接口的many = @Many多条数据处理
//其实由这里可以看出来多对一和一对多是差不多的看是哪一个为主体
//这里返回来处理的是集合所以javaType给到的是List.call 数据做处理的也是@Many注解(注意many=@Many别误解为多对多)
@Select("select * from classes")
@Results({
@Result(property = "cid",column = "cid"),
@Result(property = "cname",column = "cname"),
@Result(property = "stuList",javaType = List.class,column = "cid",
many = @Many(select = "com.etime.mapper.StudentMapper.getStudentByCid"))
})
List<Classes> getAllClasses();
}
由上可知的一对多的方式进行的数据查询可以看出,其实两种方式都差不多,只是看是怎么看他们之间的关系型,以谁为主体
众所周知的学生和课程之间是常见的多对多的关系,接下来就以学生关系来测试多对多的关系
创建课程实体类
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@Data
@NoArgsConstructor
public class Course {
private int courseid;
private String coursename;
}
创建学生实体类对象
Student.java
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Student implements Serializable {
private int sid;
private String sname;
private int cid;
private List<StudentCourse> studentCourseList;
}
创建学生和课程的关系表的实体类
StudentCourse.java
package com.etime.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class StudentCourse {
private int scid;
private int sid;
private int courseid;
private Course course;
}
创建课程CourseMapper.java接口查询数据
package com.etime.mapper;
import com.etime.pojo.Course;
import org.apache.ibatis.annotations.Select;
public interface CourseMapper {
//由课程id到课程表中进行查询课程信息
@Select("select * from course where courseid=#{courseid}")
Course getCourseByCourseid(int courseid);
}
创建学生StudentMapper.java接口查询数据处理
package com.etime.mapper;
import com.etime.pojo.Classes;
import com.etime.pojo.Student;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface StudentMapper {
//使用@Select查询所有学生信息
//通过@Result注解中的属性many = @Many进行数据处理,将得到的课程对象返回到学生实体类对象studentCourseList中
@Select("select * from student")
@Results({
@Result(property = "sid",column = "sid"),
@Result(property = "sname",column = "sname"),
@Result(property = "cid",column = "cid"),
@Result(property = "studentCourseList",javaType = List.class,column = "sid",
many = @Many(select = "com.etime.mapper.StudentCourseMapper.getStudentCourseBySid"))
})
List<Student> getStudentAndCourse();
}
创建学生课程接口StudentCourseMapper.java查询数据,对数据进行处理
package com.etime.mapper;
import com.etime.pojo.Course;
import com.etime.pojo.StudentCourse;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface StudentCourseMapper {
//@Select同样的通过注解的方式对数据进行全部查询,处理的通过id查询到的课程单个实体类
@Select("select * from studentcourse where sid=#{sid}")
@Results({
@Result(property = "scid",column = "scid"),
@Result(property = "sid",column = "sid"),
@Result(property = "courseid",column = "courseid"),
@Result(property = "course",javaType = Course.class,column = "courseid",
one = @One(select = "com.etime.mapper.CourseMapper.getCourseByCourseid"))
})
//根据学生id查找所有课程对应的id
List<StudentCourse> getStudentCourseBySid(int sid);
}