您的位置:首页 > 房产 > 家装 > 第三章 数据访问:JPA关联MyBatis

第三章 数据访问:JPA关联MyBatis

2024/12/23 1:25:26 来源:https://blog.csdn.net/2301_78884095/article/details/141901819  浏览:    关键词:第三章 数据访问:JPA关联MyBatis

学习目标

  • 3.1 JPA多表查询
    • 3.1.1 数据库表及关系
    • 3.1.2 多表联接查询
    • 3.1.3 关联映射
      • 3.1.3.1 单向多对一关联
      • 3.1.3.2 双向一对多关联
  • 3.2 Spring Boot 集成 MyBAtis
    • 3.2.1 回顾MyBatis
    • 3.2.2 Spring Boot 集成MyBAtis
      • 3.2.2.1 MyBAtis-Spring-Boot-Starter
      • 3.2.2.2 XML配置版集成
      • 3.2.2.3 注解配置版集成

  (如果没有了解可以去我主页看看 第一至二章的内容来学习)我们已经对JPA的基本概念、对象/关系映射注解,仅仅通过少量配置实现单表大部分CRUD操作,进行分页,自定义QL语句,自定义动态查询完成复杂查询,本章我们继续学习JPA高级应用,掌握多表联接查询,以及通过关联映射完成延迟加载、级联操作等。
  本章我们除了JPA的多表联接查询、关联映射外,还会在后半部分快速完成Spring Boot 和MyBatis的集成开发,包括基于XML配置和注解配置两种方式。

3.1 JPA多表查询

  多表查询在 Spring Data JPA 中有两种实现方式,第一种是创建一个结果集的接口来接收多表接查询后的结果,第二种是利用JPA的关联映射来实现。

3.1.1 数据库表及关系

  我们通常不直接编写代码来定义数据库表及其关系,因为这些操作更多是在数据库层面(如使用SQL语句)或通过ORM(对象关系映射)框架来完成的。不过,我可以向你展示如何使用Java代码结合JDBC(Java Database Connectivity)来执行SQL语句,这些SQL语句可以创建表、定义关系等。

使用JDBC创建表和关系
  以下是一个简单的Java示例,展示了如何使用JDBC连接数据库并执行SQL语句来创建表及定义表之间的关系。

import java.sql.Connection;  
import java.sql.DriverManager;  
import java.sql.Statement;  public class DatabaseExample {  public static void main(String[] args) {  // 数据库连接信息(请根据你的数据库进行更改)  String url = "jdbc:mysql://localhost:3306/yourdatabase";  String user = "username";  String password = "password";  try {  // 加载并注册JDBC驱动  Class.forName("com.mysql.cj.jdbc.Driver");  // 建立连接  Connection conn = DriverManager.getConnection(url, user, password);  // 创建Statement对象来执行SQL语句  Statement stmt = conn.createStatement();  // 创建表  String sqlCreateTable1 = "CREATE TABLE IF NOT EXISTS Student (" +  "id INT AUTO_INCREMENT PRIMARY KEY, " +  "name VARCHAR(255) NOT NULL)";  String sqlCreateTable2 = "CREATE TABLE IF NOT EXISTS Course (" +  "id INT AUTO_INCREMENT PRIMARY KEY, " +  "name VARCHAR(255) NOT NULL)";  // 创建关系表  String sqlCreateRelationTable = "CREATE TABLE IF NOT EXISTS Enrollment (" +  "student_id INT, " +  "course_id INT, " +  "FOREIGN KEY (student_id) REFERENCES Student(id), " +  "FOREIGN KEY (course_id) REFERENCES Course(id), " +  "PRIMARY KEY (student_id, course_id))";  // 执行SQL语句  stmt.executeUpdate(sqlCreateTable1);  stmt.executeUpdate(sqlCreateTable2);  stmt.executeUpdate(sqlCreateRelationTable);  // 关闭资源  stmt.close();  conn.close();  System.out.println("Tables and relations created successfully");  } catch (Exception e) {  e.printStackTrace();  }  }  
}

使用JPA或Hibernate
  在Java中,更常见的方式是使用JPA(Java Persistence API)或Hibernate这样的ORM框架来管理数据库表及关系。这些框架允许你通过定义Java类(实体)及其关系来映射到数据库表及表之间的关系。

Hibernate很多公司都会用MyBatis代替它,Hibernate是一个对象关系映射(ORM)框架,它允许Java开发者以面向对象的方式与数据库进行交互,减少了直接编写SQL的需求。Hibernate的优点包括简化数据库操作、支持多种数据库、高性能的事务管理、高度灵活的查询语言HQL(Hibernate Query Language),以及良好的数据库无关性。此外,Hibernate还提供了缓存和延迟加载等技术,有效地提高了数据访问的性能,并保证了数据的一致性‌。

3.1.2 多表联接查询

  在Spring Data JPA中,你可以通过@Query注解在Repository接口中定义自定义的JPQL(Java Persistence Query Language)查询,或者使用@EntityGraph来优化加载关联数据。但是,直接在JPQL中编写多表联接查询是更常见的做法。

  假设你有两个实体类Student和Course,以及一个联接表Enrollment(虽然通常联接逻辑是通过实体关系来处理的,而不是物理的联接表)。

@Entity  
public class Student {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  private String name;  // 省略getter和setter  @OneToMany(mappedBy = "student")  private List<Enrollment> enrollments;  
}  @Entity  
public class Course {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  private String name;  // 省略getter和setter  @OneToMany(mappedBy = "course")  private List<Enrollment> enrollments;  
}  @Entity  
@Table(name = "enrollment")  
public class Enrollment {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  @ManyToOne  @JoinColumn(name = "student_id")  private Student student;  @ManyToOne  @JoinColumn(name = "course_id")  private Course course;  // 省略getter和setter  
}

然后,在你的Repository接口中,你可以定义一个自定义查询来执行多表联接:

public interface StudentRepository extends JpaRepository<Student, Long> {  @Query("SELECT s FROM Student s JOIN FETCH s.enrollments e JOIN FETCH e.course c WHERE c.name = :courseName")  List<Student> findStudentsByCourseName(@Param("courseName") String courseName);  
}

注意:上面的查询使用了JOIN FETCH来优化加载关联数据,但请注意,这可能会根据数据库和JPA提供者的不同而有所不同,并且可能不是所有情况下都是最优选择。

3.1.3 关联映射

  在Spring Boot中,关联映射通常是通过JPA(Java Persistence API)来实现的,它允许你定义实体之间的关系,并通过这些关系来映射数据库中的表。以下是一个简单的示例,展示了如何在Spring Boot项目中使用JPA来定义实体之间的关联映射。

  假设我们有两个实体:Student 和 Course,以及一个关联表 Enrollment(虽然在实际操作中,JPA通常通过实体关系来管理关联,而不是物理的联接表,但这里为了说明目的,我们假设有这样一个表)。然而,在JPA中,我们通常不会直接映射到物理的联接表,而是会通过实体之间的关联关系来隐含这个联接。

首先,我们定义Student和Course实体,以及它们之间的关联:

import javax.persistence.*;  
import java.util.List;  @Entity  
public class Student {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  private String name;  // 使用@OneToMany注解来映射与Enrollment的关联关系  // mappedBy属性指向Enrollment中拥有当前实体引用的属性名  @OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true)  private List<Enrollment> enrollments;  // 省略getter和setter  
}  @Entity  
public class Course {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  private String name;  // 使用@OneToMany注解来映射与Enrollment的关联关系  // mappedBy属性指向Enrollment中拥有当前实体引用的属性名  @OneToMany(mappedBy = "course", cascade = CascadeType.ALL, orphanRemoval = true)  private List<Enrollment> enrollments;  // 省略getter和setter  
}  @Entity  
@Table(name = "enrollment")  
public class Enrollment {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  @ManyToOne  @JoinColumn(name = "student_id")  private Student student;  @ManyToOne  @JoinColumn(name = "course_id")  private Course course;  // 省略getter和setter  
}

在上面的代码中,Student 和 Course 实体都通过 @OneToMany 注解与 Enrollment 实体建立了关联。

注意:mappedBy 属性在 @OneToMany 注解中用于指定关系的“所有者”端,即拥有外键的那个实体。在这个例子中,Enrollment 实体包含了指向 Student 和 Course 的外键,因此 mappedBy 指向了 Enrollment 实体中相应的属性名。

3.1.3.1 单向多对一关联

  在Spring Boot中,使用JPA(Java Persistence API)来实现单向多对一(Many-To-One)关联映射是一种常见的做法。单向多对一意味着一个实体(多的一方)包含对另一个实体(一的一方)的引用,但反过来则不然。

  以下是一个简单的示例,展示了如何在Spring Boot项目中定义单向多对一关联映射的Java代码。

首先,我们定义“一的一方”实体,比如Course:

import javax.persistence.*;  @Entity  
public class Course {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  private String name;  // 通常不需要额外的字段或方法来维护多对一关系,因为这是一端  // 省略getter和setter  
}

然后,我们定义“多的一方”实体,比如Student,并包含对Course的引用:

import javax.persistence.*;  @Entity  
public class Student {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  private String name;  // 单向多对一关联  @ManyToOne  @JoinColumn(name = "course_id") // 指定外键列名  private Course course;  // getter和setter  public Long getId() {  return id;  }  public void setId(Long id) {  this.id = id;  }  public String getName() {  return name;  }  public void setName(String name) {  this.name = name;  }  public Course getCourse() {  return course;  }  public void setCourse(Course course) {  this.course = course;  }  
}

  在这个例子中,Student实体通过@ManyToOne注解与Course实体建立了单向多对一关联。@JoinColumn注解用于指定数据库中用于连接这两个表的外键列名(在这个例子中是course_id)。这意味着在Student表中将会有一个名为course_id的列,该列是Course表主键的外键。

请注意:由于这是单向关联,所以Course实体中不包含对Student实体的任何引用或集合。如果你需要双向关联(即Course实体也知道它有哪些学生),你需要在Course实体中添加一个@OneToMany注解的集合,并设置mappedBy属性指向Student实体中维护关联的字段名(在这个单向关联的示例中,我们不会这样做)。

3.1.3.2 双向一对多关联

  在Spring Boot中,使用JPA(Java Persistence API)来实现双向一对多(One-To-Many)关联映射是一种常见的做法。双向关联意味着两个实体都包含对彼此的引用。以下是一个简单的示例,展示了如何在Spring Boot项目中定义双向一对多关联映射的Java代码。

首先,我们定义“一的一方”实体,比如Course,它包含一个指向多个Student的集合:

import javax.persistence.*;  
import java.util.HashSet;  
import java.util.Set;  @Entity  
public class Course {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  private String name;  // 双向一对多关联:多的一端在Student中,这里使用mappedBy来指定关系的维护端在Student  @OneToMany(mappedBy = "course", cascade = CascadeType.ALL, orphanRemoval = true)  private Set<Student> students = new HashSet<>();  // 省略getter和setter,但请确保为students提供getter和setter  // 添加学生的方法,用于维护双向关系  public void addStudent(Student student) {  this.students.add(student);  student.setCourse(this); // 维护双向关系  }  // 移除学生的方法,也用于维护双向关系  public void removeStudent(Student student) {  this.students.remove(student);  student.setCourse(null); // 维护双向关系  }  
}

然后,我们定义“多的一方”实体,比如Student,它包含一个对Course的引用:

import javax.persistence.*;  @Entity  
public class Student {  @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Long id;  private String name;  // 双向一对多关联:一的一端在Course中  @ManyToOne  @JoinColumn(name = "course_id")  private Course course;  // 省略getter和setter  // 设置课程时,如果这是双向关联,通常不需要额外的操作,因为JPA会处理它  // 但如果你需要在设置课程时执行一些自定义逻辑,可以在这里添加  public void setCourse(Course course) {  this.course = course;  }  
}

  在这个例子中,Course实体通过@OneToMany注解与Student实体建立了双向一对多关联,并通过mappedBy属性指定了关系的维护端在Student实体中(即Student实体中的course字段)。cascade = CascadeType.ALL表示对Course实体执行的操作(如保存、更新、删除)将级联到其关联的Student实体上。orphanRemoval = true表示如果Student实体不再被Course实体引用(即从students集合中移除),则这些Student实体也将被从数据库中删除。

请注意:为了维护双向关系的一致性,我们在Course实体中提供了addStudent和removeStudent方法,这些方法在添加或移除学生时也会更新学生实体中的course字段。这是确保双向关系一致性的关键步骤。

3.2 Spring Boot 集成 MyBAtis

3.2.1 回顾MyBatis

  MyBatis是一款标准的ORM框架,被广泛的应用于各企业开发中。MyBAtis最早是Apache的一个开源项目 iBatis,2010年这个项目由 Apache Software Foundation迁移到了Google Code,并且改名为 MyBAtis,2013年11月又迁移到Github。
优点:

  • SQL被统一提取出来,便于统一管理和优化。
  • SQL和代码解耦,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰、更易维护、更易单元测试。
  • 提供映射标签,支持对象与数据库的ORM字段关系映射
  • 提供对象关系映射标签,支持对象关系组件维护
  • 灵活书写动态SQL,支持各种条件来动态生成不同的SQL

缺点:

  • 编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此
  • SQL语句依赖于数据库,导致数据库移植性差

3.2.2 Spring Boot 集成MyBAtis

3.2.2.1 MyBAtis-Spring-Boot-Starter

MyBAtis-Spring-Boot-Starter是MyBAtis帮助我们快速集成Spring Boot 提供的一个组件包,使用这个组件可以做到以下几点:

  • 构建独立的应用
  • 几乎可以零配置
  • 需要很少的XML配置

3.2.2.2 XML配置版集成

要在Spring Boot项目中使用XML配置,你需要做几件事情:

  1. 引入Spring XML配置支持: 确保你的pom.xml(Maven)或build.gradle(Gradle)文件中包含了Spring Boot的起步依赖,并且可能还需要添加Spring的XML配置支持依赖(尽管这通常不是必需的,因为Spring Boot的starter已经包含了这些)。
  2. 创建XML配置文件: 在你的src/main/resources目录下创建XML配置文件,比如applicationContext.xml。
  3. 在Java配置中导入XML配置: 使用@ImportResource注解来导入你的XML配置文件。这可以在你的主配置类(通常带有@SpringBootApplication注解的类)上完成,或者在任何其他配置类上。

下面是一个简单的例子:

  1. Maven依赖(如果尚未包含)
    确保你的pom.xml文件中包含了Spring Boot的起步依赖。对于XML支持,通常不需要额外的依赖,因为Spring Boot的starter已经包含了必要的库。

  2. 创建XML配置文件
    在src/main/resources目录下创建applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans.xsd">  <bean id="myBean" class="com.example.MyBeanClass"/>  </beans>
  1. 在Java配置中导入XML配置
    在你的主配置类(或任何配置类)上使用@ImportResource注解来导入XML配置:
package com.example.demo;  import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.context.annotation.ImportResource;  @SpringBootApplication  
@ImportResource("classpath:applicationContext.xml")  
public class DemoApplication {  public static void main(String[] args) {  SpringApplication.run(DemoApplication.class, args);  }  
}
  1. 使用XML中定义的Bean
    现在,你可以在Spring Boot应用的任何地方通过自动装配(@Autowired)来使用myBean了,就像它是通过Java配置定义的Bean一样。

请注意,尽管你可以这样做,但通常建议尽可能使用基于注解的配置,因为它更简洁、更易于理解和维护。XML配置在需要时可以作为补充,但不应成为主要的配置方式。

3.2.2.3 注解配置版集成

  在Spring Boot中,使用注解配置(Annotation-based Configuration)是推荐的方式来管理应用的配置和Bean的定义。这种方式利用Java配置类(通过@Configuration注解标识)和@Bean注解来替代传统的XML配置文件。

以下是一个简单的Spring Boot注解配置版集成Java代码的示例:

  1. 创建一个配置类
    首先,你需要创建一个配置类,该类使用@Configuration注解来标记它是一个配置类。在这个类中,你可以定义Bean,使用@Bean注解来标注方法,Spring容器会调用这些方法并返回对象实例,然后将这些对象注册为Bean。
package com.example.demo.config;  import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  @Configuration  
public class AppConfig {  @Bean  public MyService myService() {  return new MyService();  }  
}

在这个例子中,AppConfig是一个配置类,它定义了一个Bean,即MyService的一个实例。MyService可能是一个你自定义的服务类。

  1. 自定义服务类
    接下来,定义MyService类,这个类可以包含你需要的业务逻辑。
package com.example.demo.service;  public class MyService {  public void doSomething() {  // 实现你的业务逻辑  System.out.println("Doing something...");  }  
}
  1. 使用定义的Bean
    现在,你可以在任何需要MyService的地方,通过Spring的自动装配功能(例如使用@Autowired注解)来使用它。
package com.example.demo.controller;  import com.example.demo.service.MyService;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  @RestController  
public class MyController {  private final MyService myService;  @Autowired  public MyController(MyService myService) {  this.myService = myService;  }  @GetMapping("/doSomething")  public String doSomething() {  myService.doSomething();  return "Something has been done!";  }  
}

在这个例子中,MyController是一个REST控制器,它使用@Autowired注解来自动装配MyService的实例。然后,在doSomething方法中,它调用了MyService的doSomething方法,并返回一个响应。

  1. 启动类
    确保你的Spring Boot应用有一个带有@SpringBootApplication注解的启动类。这个注解是一个方便的注解,它包含了@Configuration、@EnableAutoConfiguration和@ComponentScan注解。
package com.example.demo;  import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  @SpringBootApplication  
public class DemoApplication {  public static void main(String[] args) {  SpringApplication.run(DemoApplication.class, args);  }  
}

  这个启动类会扫描包含@SpringBootApplication注解的类所在的包及其子包下的所有组件(如配置类、控制器、服务等),并将它们注册到Spring容器中。由于AppConfig位于启动类所在包的子包中(在这个例子中是com.example.demo.config),因此它会被自动扫描并注册。

以上就是Spring Boot注解配置版集成Java代码的一个简单示例。通过这种方式,你可以完全摆脱XML配置文件,只使用Java代码来配置你的Spring应用。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com