赞
踩
对象关系映射(Object relational mapping)是指通过将对象状态映射到数据库列,来开发和维护对象和关系数据库之间的关系。它能够轻松处理(执行)各种数据库操作,如插入、更新、删除等。
关系映射的注解:
注解 | 说明 |
---|---|
@JoinColumn | 指定一个实体组织或实体集合。用在“多对一”和“一对多”的关联中。 |
@OneToOne | 定义表之间“一对一”的关系。 |
@OneToMany | 定义表之间“一对多”的关系。 |
@ManyToOne | 定义表之间“多对一”的关系。 |
@ManyToMany | 定义表之间“多对多”的关系。 |
下面将介绍如何使用Spring Data JPA处理多张数据库表之间的关联关系。
(1)创建SpringBoot项目
创建SpringBoot项目,并创建项目结构:dao(数据访问层)、entity(实体层)、test(测试层)。
(2)添加JPA和MySQL数据库的依赖
在pom.xml配置文件中,添加如下依赖:
- <!-- Spring Data JPA -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-jpa</artifactId>
- </dependency>
-
- <!-- MySQL的JDBC数据库驱动 -->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>8.0.19</version>
- </dependency>
(3)配置数据库连接信息
SpringBoot项目使用MySQL等关系型数据库,需要配置连接信息。
将默认的application.properties文件的后缀修改为“.yml”,即配置文件名称为:application.yml,并配置以下MySQL数据库的连接信息:
- spring:
- datasource:
- url: jdbc:mysql://localhost:3306/db_admin?useSSL=false&
- username: root
- password: 123456
- driver-class-name: com.mysql.cj.jdbc.Driver
- jpa:
- database: MYSQL
- show-sql: true
- open-in-view: true
- properties:
- hibernate:
- enable_lazy_load_no_trans: true #使用延时加载时控制Session的生命周期
- dialect: org.hibernate.dialect.MySQL5Dialect
- ddl-auto: update
在Spring Data JPA中在使用延时加载时,要控制Session的生命周期,否则会出现“could not initialize proxy [xxxxxx#1] - no Session”错误。可以在配置文件中配置以下代码来控制Session的生命周期:
application.properties配置文件:
- spring.jpa.open-in-view=true
- spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
application.yml配置文件:
一对一外键关联的配置比较简单,以公民实体对象和身份证实体对象为例,在表tab_people(公民表)中添加一个字段“card_id”,作为该表的外键,同时需要保证该字段的唯一性,否则就不是一对一映射关系了,而是一对多映射关系。表tab_people和tab_idcard(身份证表)之间的关联关系如下图所示。
【示例】一对一关联,获取公民信息与身份证号码。
(1)在MySQL数据库创建tab_people表和tab_idcard表,并添加相关数据。
- -- 判断数据表是否存在,存在则删除
- DROP TABLE IF EXISTS tab_people;
- DROP TABLE IF EXISTS tab_idcard;
-
- -- 创建“身份证信息”数据表
- CREATE TABLE IF NOT EXISTS tab_idcard
- (
- id INT AUTO_INCREMENT PRIMARY KEY COMMENT '身份证ID',
- idCard_code VARCHAR(45) COMMENT '身份证号码'
- ) COMMENT = '身份证信息表';
-
- -- 创建“公民信息”数据表
- CREATE TABLE IF NOT EXISTS tab_people
- (
- id INT AUTO_INCREMENT PRIMARY KEY COMMENT '公民ID',
- NAME VARCHAR(45) NOT NULL COMMENT '公民名称',
- sex VARCHAR(2) COMMENT '公民性别',
- age INT COMMENT '公民年龄',
- card_id INT UNIQUE COMMENT '身份证ID',
- -- 创建外键约束
- FOREIGN KEY fk_card_id (card_id)
- REFERENCES tab_idcard(id)
- ) COMMENT = '公民信息表';
-
- -- 添加数据
- INSERT INTO tab_idcard(idCard_code) VALUE('123456789');
- INSERT INTO tab_people(NAME,sex,age,card_id) VALUES('pan_junbiao的博客','男',32,1);
(2)创建名称为People.java公民信息的持久化类。
- package com.pjb.jpauserdemo.entity;
-
- import javax.persistence.*;
-
- /**
- * 公民信息的持久化类
- * @author pan_junbiao
- **/
- @Entity
- @Table(name = "tab_people")
- public class People
- {
- //公民ID
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "id")
- private int id;
-
- //公民名称
- @Column(name = "name")
- private String name;
-
- //公民性别
- @Column(name = "sex")
- private String sex;
-
- //公民年龄
- @Column(name = "age")
- private int age;
-
- //关联的身份证对象
- @OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
- @JoinColumn(name="card_id")
- private IDcard idcard;
-
- public int getId()
- {
- return id;
- }
-
- public void setId(int id)
- {
- this.id = id;
- }
-
- public String getName()
- {
- return name;
- }
-
- public void setName(String name)
- {
- this.name = name;
- }
-
- public String getSex()
- {
- return sex;
- }
-
- public void setSex(String sex)
- {
- this.sex = sex;
- }
-
- public int getAge()
- {
- return age;
- }
-
- public void setAge(int age)
- {
- this.age = age;
- }
-
- public IDcard getIdcard()
- {
- return idcard;
- }
-
- public void setIdcard(IDcard idcard)
- {
- this.idcard = idcard;
- }
- }
(3)创建名称为IDcard.java身份证信息的持久化类。
- package com.pjb.jpauserdemo.entity;
-
- import javax.persistence.*;
-
- /**
- * 身份证信息的持久化类
- * @author pan_junbiao
- **/
- @Entity
- @Table(name = "tab_idcard")
- public class IDcard
- {
- //身份证ID
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "id")
- private int id;
-
- //身份证号码
- @Column(name = "idcard_code")
- private String idCardCode;
-
- public int getId()
- {
- return id;
- }
-
- public void setId(int id)
- {
- this.id = id;
- }
-
- public String getIdCardCode()
- {
- return idCardCode;
- }
-
- public void setIdCardCode(String idCardCode)
- {
- this.idCardCode = idCardCode;
- }
- }
(4)创建名称为PeopleDao.java公民信息数据库访问接口,并继承JpaRepository接口。
- package com.pjb.jpauserdemo.dao;
-
- import com.pjb.jpauserdemo.entity.People;
- import org.springframework.data.jpa.repository.JpaRepository;
- import org.springframework.stereotype.Repository;
-
- /**
- * 公民信息数据库访问接口
- * @author pan_junbiao
- **/
- @Repository
- public interface PeopleDao extends JpaRepository<People,Integer>
- {
- }
(5) 测试:一对一新增(新增公民信息与关联的身份证信息)。
- @Autowired
- private PeopleDao peopleDao;
-
- /**
- * 一对一新增:新增公民信息与关联的身份证信息
- * @author pan_junbiao
- */
- @Test
- public void addPeopleAndIdcard()
- {
- //创建身份证信息
- IDcard idcard = new IDcard();
- idcard.setIdCardCode("123456789");
-
- //创建公民信息
- People people = new People();
- people.setName("pan_junbiao的博客");
- people.setSex("男");
- people.setAge(32);
- //将公民与身份证信息关联
- people.setIdcard(idcard);
-
- //执行新增操作
- peopleDao.save(people);
-
- //如果新增成功,则可以获取自增主键
- //否则新增失败,则抛出异常
- if(people.getId()>0)
- {
- System.out.println("新增公民信息成功!");
- System.out.println("公民ID:" + people.getId());
- System.out.println("公民名称:" + people.getName());
- System.out.println("身份证号码:" + idcard.getIdCardCode());
- }
- }
执行结果:
(6)测试:一对一查询(获取公民信息与关联的身份证信息)。
- @Autowired
- private PeopleDao peopleDao;
-
- /**
- * 一对一查询:获取公民信息与关联的身份证信息
- * @author pan_junbiao
- */
- @Test
- public void getPeopleAndIdcard()
- {
- People people = peopleDao.findById(1).get();
- if (people != null)
- {
- System.out.println("---------------1、公民信息--------------------");
- System.out.println("公民编号:" + people.getId());
- System.out.println("公民名称:" + people.getName());
- System.out.println("公民性别:" + people.getSex());
- System.out.println("公民年龄:" + people.getAge());
-
- //获取关联的身份证信息信息
- System.out.println("---------------2、身份证信息信息---------------");
- IDcard idCard = people.getIdcard();
- if(idCard!=null)
- {
- System.out.println("身份证ID:" + idCard.getId());
- System.out.println("身份证号码:" + idCard.getIdCardCode());
- }
- }
- }
执行结果:
在日常开发中一对多查询是常见的,也是业务中十分重要的部分。下面将以生产商对象(类Factory)与产品对象(类Product)为例,讲解JPA的一对多关联。类Factory与类Product的关联关系如下图。
【示例】建立生产商与产品对象对象的一对多关联,并利用映射关系查询完整的产品信息。
(1)在MySQL数据库创建tab_factory表和tab_product表,并添加相关数据。
- -- 判断数据表是否存在,存在则删除
- DROP TABLE IF EXISTS tab_factory;
- DROP TABLE IF EXISTS tab_product;
-
- -- 创建“生产商信息”数据表
- CREATE TABLE IF NOT EXISTS tab_factory
- (
- factory_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '生产商ID',
- NAME VARCHAR(20) NOT NULL COMMENT '生产商名称'
- ) COMMENT = '生产商信息表';
-
- -- 创建“产品信息”数据表
- CREATE TABLE IF NOT EXISTS tab_product
- (
- id INT AUTO_INCREMENT PRIMARY KEY COMMENT '产品ID',
- NAME VARCHAR(20) NOT NULL COMMENT '产品名称',
- price DECIMAL(9,2) NOT NULL COMMENT '产品价格',
- factory_id INT COMMENT '生产商ID'
- ) COMMENT = '产品信息表';
-
- -- 添加数据
- INSERT INTO tab_factory(NAME) VALUES('华为公司');
- INSERT INTO tab_product(NAME,price,factory_id) VALUES('华为手机',1299,1);
- INSERT INTO tab_product(NAME,price,factory_id) VALUES('华为路由器',699,1);
(2)创建名称为Product.java产品信息的持久化类。
- package com.pjb.jpauserdemo.entity;
-
- import javax.persistence.*;
- import java.math.BigDecimal;
-
- /**
- * 产品信息的持久化类
- * @author pan_junbiao
- **/
- @Entity
- @Table(name = "tab_product")
- public class Product
- {
- //产品ID
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "id")
- private int id;
-
- //产品名称
- @Column(name = "name")
- private String name;
-
- //产品价格
- @Column(name = "price")
- private BigDecimal price;
-
- //生产商信息
- @ManyToOne
- @JoinColumn(name = "factory_id")
- private Factory factory;
-
- public int getId()
- {
- return id;
- }
-
- public void setId(int id)
- {
- this.id = id;
- }
-
- public String getName()
- {
- return name;
- }
-
- public void setName(String name)
- {
- this.name = name;
- }
-
- public BigDecimal getPrice()
- {
- return price;
- }
-
- public void setPrice(BigDecimal price)
- {
- this.price = price;
- }
-
- public Factory getFactory()
- {
- return factory;
- }
-
- public void setFactory(Factory factory)
- {
- this.factory = factory;
- }
- }
(3)创建名称为Factory.java生产商信息的持久化类。
- package com.pjb.jpauserdemo.entity;
-
- import javax.persistence.*;
- import java.util.List;
-
- /**
- * 生产商信息的持久化类
- * @author pan_junbiao
- **/
- @Entity
- @Table(name = "tab_factory")
- public class Factory
- {
- //生产商ID
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "factory_id")
- private int factoryId;
-
- //生产商名称
- @Column(name = "name")
- private String name;
-
- //产品列表
- @OneToMany
- @JoinColumn(name = "factory_id")
- private List<Product> productList;
-
- public int getFactoryId()
- {
- return factoryId;
- }
-
- public void setFactoryId(int factoryId)
- {
- this.factoryId = factoryId;
- }
-
- public String getName()
- {
- return name;
- }
-
- public void setName(String name)
- {
- this.name = name;
- }
-
- public List<Product> getProductList()
- {
- return productList;
- }
-
- public void setProductList(List<Product> productList)
- {
- this.productList = productList;
- }
- }
(4)创建名称为FactoryDao.java生产商信息数据库访问接口,并继承JpaRepository接口。
- package com.pjb.jpauserdemo.dao;
-
- import com.pjb.jpauserdemo.entity.Factory;
- import org.springframework.data.jpa.repository.JpaRepository;
- import org.springframework.stereotype.Repository;
-
- /**
- * 生产商信息数据库访问接口
- * @author pan_junbiao
- **/
- @Repository
- public interface FactoryDao extends JpaRepository<Factory,Integer>
- {
- }
(5)创建名称为ProductDao.java产品信息数据库访问接口,并继承JpaRepository接口。
- package com.pjb.jpauserdemo.dao;
-
- import com.pjb.jpauserdemo.entity.Product;
- import org.springframework.data.jpa.repository.JpaRepository;
- import org.springframework.stereotype.Repository;
-
- /**
- * 产品信息数据库访问接口
- * @author pan_junbiao
- **/
- @Repository
- public interface ProductDao extends JpaRepository<Product,Integer>
- {
- }
(6)测试:一对多查询(获取生产商信息与关联的产品列表)。
- @Autowired
- private FactoryDao factoryDao;
-
- /**
- * 一对多查询:获取生产商信息与关联的产品列表
- * @author pan_junbiao
- */
- @Test
- public void getFactoryAndProductList()
- {
- Factory factory = factoryDao.findById(1).get();
- if (factory != null)
- {
- System.out.println("---------------1、生产商信息信息--------------");
- System.out.println("生产商编号:" + factory.getFactoryId());
- System.out.println("生产商名称:" + factory.getName());
-
- //获取关联的产品信息信息
- System.out.println("---------------2、产品信息信息---------------");
- List<Product> productList = factory.getProductList();
- if(productList!=null && productList.size()>0)
- {
- for(Product product : productList)
- {
- System.out.println("产品编号:" + product.getId());
- System.out.println("产品名称:" + product.getName());
- System.out.println("产品价格:" + product.getPrice());
- System.out.println("-------------------");
- }
- }
- }
- }
执行结果:
(7)测试:多对一查询(获取产品信息与关联的生产商信息)。
- @Autowired
- private ProductDao productDao;
-
- /**
- * 多对一查询:获取产品信息与关联的生产商信息
- * @author pan_junbiao
- */
- @Test
- public void getProductAndFactory()
- {
- List<Product> productList = productDao.findAll();
- if(productList!=null && productList.size()>0)
- {
- for (Product product : productList)
- {
- //获取产品信息
- System.out.println("产品编号:" + product.getId());
- System.out.println("产品名称:" + product.getName());
- System.out.println("产品价格:" + product.getPrice());
-
- //获取关联的生产商信息信息
- Factory factory = product.getFactory();
- System.out.println("生产商编号:" + factory.getFactoryId());
- System.out.println("生产商名称:" + factory.getName());
- System.out.println("-------------------");
- }
- }
- }
执行结果:
多对多关联关系是比较特殊的一种关联关系,它与一对一和一对多关联关系不同,需要通过另外的一张表保存多对多的映射关系。下面将以应用系统中的权限分配为例讲解多对多的关联关系,例如用户可以拥有多个系统的操作权限,而一个权限又可以被赋予多个用户,这就是典型的多对多关联映射关系。其中用户表(tab_user)和权限表(tab_user)的表关系如下图所示。
说明:由于多对多关联关系的查询对第3个表进行反复查询,在一定程度上会影响系统的性能效率,所以在应用中尽量少使用多对多关联关系的表结果。
【示例】建立用户对象与权限对象的多对多关联关系,查询用户admin所拥有的权限,以及权限“新闻管理”被赋予了哪些用户。
(1)在MySQL数据库创建用户表(tab_user)、权限表(tab_role)和映射表(tab_mapping),并添加相关数据。
- -- 判断数据表是否存在,存在则删除
- DROP TABLE IF EXISTS tab_user;
- DROP TABLE IF EXISTS tab_role;
- DROP TABLE IF EXISTS tab_mapping;
-
- -- 创建“用户信息”数据表
- CREATE TABLE IF NOT EXISTS tab_user
- (
- id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
- NAME VARCHAR(45) NOT NULL COMMENT '用户名称'
- ) COMMENT = '用户信息表';
-
- -- 创建“权限信息”数据表
- CREATE TABLE IF NOT EXISTS tab_role
- (
- id INT AUTO_INCREMENT PRIMARY KEY COMMENT '权限ID',
- role_name VARCHAR(45) NOT NULL COMMENT '权限名称'
- ) COMMENT = '权限信息表';
-
- -- 创建“映射信息”数据表
- CREATE TABLE IF NOT EXISTS tab_mapping
- (
- id INT AUTO_INCREMENT PRIMARY KEY COMMENT '映射ID',
- user_id INT COMMENT '用户Id',
- role_id INT COMMENT '权限Id'
- ) COMMENT = '映射信息表';
-
- -- 添加数据
- INSERT INTO tab_user(NAME) VALUES('admin'),('pan_junbiao的博客');
- INSERT INTO tab_role(role_name) VALUES('系统管理员'),('新闻管理员'),('广告管理员');
- INSERT INTO tab_mapping(user_id,role_id) VALUES(1,1),(1,2),(1,3),(2,2),(2,3);
(2)创建名称为User.java用户信息的持久化类。
- package com.pjb.jpauserdemo.entity;
-
- import javax.persistence.*;
- import java.util.List;
-
- /**
- * 用户信息的持久化类
- * @author pan_junbiao
- **/
- @Entity
- @Table(name = "tab_user")
- public class User
- {
- //用户ID
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "id")
- private int id;
-
- //用户名称
- @Column(name = "name")
- private String name;
-
- //引用的权限实体对象集合
- @ManyToMany(fetch = FetchType.LAZY)
- @JoinTable(name = "tab_mapping",joinColumns = {@JoinColumn(name = "user_id")},inverseJoinColumns = {@JoinColumn(name="role_id")})
- private List<Role> roleList;
-
- public int getId()
- {
- return id;
- }
-
- public void setId(int id)
- {
- this.id = id;
- }
-
- public String getName()
- {
- return name;
- }
-
- public void setName(String name)
- {
- this.name = name;
- }
-
- public List<Role> getRoleList()
- {
- return roleList;
- }
-
- public void setRoleList(List<Role> roleList)
- {
- this.roleList = roleList;
- }
- }
(3)创建名称为Role.java权限信息的持久化类。
- package com.pjb.jpauserdemo.entity;
-
- import javax.persistence.*;
-
- /**
- * 权限信息的持久化类
- * @author pan_junbiao
- **/
- @Entity
- @Table(name = "tab_role")
- public class Role
- {
- //权限ID
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "id")
- private int id;
-
- //权限名称
- @Column(name = "role_name")
- private String roleName;
-
- 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;
- }
- }
(4)创建名称为UserDao.java用户信息数据库访问接口,并继承JpaRepository接口。
- package com.pjb.jpauserdemo.dao;
-
- import com.pjb.jpauserdemo.entity.User;
- import org.springframework.data.jpa.repository.JpaRepository;
- import org.springframework.stereotype.Repository;
-
- /**
- * 用户信息数据库访问接口
- * @author pan_junbiao
- **/
- @Repository
- public interface UserDao extends JpaRepository<User,Integer>
- {
- }
(5)测试:多对多查询(获取用户信息与关联的权限列表)。
- @Autowired
- private UserDao userDao;
-
- /**
- * 多对多查询:获取用户信息与关联的权限列表
- * @author pan_junbiao
- */
- @Test
- public void getUserAndRole()
- {
- List<User> userList = userDao.findAll();
- if(userList!=null && userList.size()>0)
- {
- //遍历用户列表
- for(User user : userList)
- {
- System.out.println("用户编号:" + user.getId());
- System.out.println("用户名称:" + user.getName());
-
- //获取权限列表
- List<Role> roleList = user.getRoleList();
- if(roleList!=null && roleList.size()>0)
- {
- System.out.print("用户拥有的权限:");
- for (Role role : roleList)
- {
- System.out.print(role.getRoleName()+";");
- }
- }
- System.out.println("\n-----------------------------------------------");
- }
- }
- }
执行结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。