Spring Boot JPA / Hibernate @Embeddable 和 @Embedded 示例

x33g5p2x  于2021-10-20 转载在 Spring  
字(7.3k)|赞(0)|评价(0)|浏览(361)

在本文中,您将学习如何在 hibernate 中使用 Embeddable 类型将线性数据库表映射到由各种值类型组成的实体类。

考虑下表存储有关应用程序用户的信息。信息包括用户的基本详细信息,如姓名、电子邮件和他的地址 -

从数据库的角度来看,表结构很好。但是如果我们需要将此表映射到我们应用程序中的实体类中,我们可能希望将 NameAddress 详细信息抽象到单独的类中,以实现更好的抽象和可重用性 -

可嵌入类型可以由任意数量的实体在您的项目中重复使用。我们将从头开始创建一个项目,并学习如何将上述 users 表映射到一个实体类,该实体类将名称和地址详细信息抽象为单独的类。

创建应用程序

我们将使用 Spring Boot CLI 来生成我们的应用程序。打开终端并键入以下命令以生成应用程序 -

spring init -n=jpa-embeddable-demo -d=web,jpa,mysql --package-name=com.example.jpa jpa-embeddable-demo

您还可以使用 Spring Initializr 网络应用程序生成应用程序。按照以下说明使用 Spring Initializr Web 工具生成应用程序 -

  1. 前往http://start.spring.io
    1.输入Artifact为“jpa-embeddable-demo”
  2. 单击 Options 下拉列表以查看与项目元数据相关的所有选项。
  3. 包名称更改为“com.example.jpa”
  4. 选择WebJPAMysql 依赖项。
    1、点击Generate生成并下载工程。

以下是完整应用的目录结构,供大家参考——

此时,您的引导项目将没有 modelrepository 包以及所有其他类。我们将在进行下一部分时创建它们。

配置数据库和休眠日志级别

我们需要配置 MySQL 数据库 URL、用户名和密码。打开 src/main/resources/application.properties 文件并向其添加以下属性 -

# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url=jdbc:mysql://localhost:3306/jpa_embeddable_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=root
spring.datasource.password=root

# Hibernate

# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE

我已经将 Hibernate 的 ddl-auto 属性指定为 update。每当我们更新应用程序中相应的实体类时,这将自动创建/更新数据库表。

请注意,您需要创建一个名为 jpa_embeddable_demo 的数据库。另外,不要忘记根据您的 MySQL 安装更改 spring.datasource.usernamespring.datasource.password 属性。

定义域模型

让我们首先定义将嵌入 User 模型中的可嵌入类型。我们将在 com.example.jpa 包中创建一个名为 model 的包,并将所有模型类添加到该包中。

可嵌入类型

我们使用 JPA 的 @Embeddable 注释来声明一个类旨在被其他实体嵌入。

1.姓名

package com.example.jpa.model;

import javax.persistence.Embeddable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Embeddable
public class Name {
    @NotNull
    @Size(max = 40)
    private String firstName;

    @Size(max = 40)
    private String middleName;

    @Size(max = 40)
    private String lastName;

    public Name() {

    }

    public Name(String firstName, String middleName, String lastName) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
    }

    // Getters and Setters (Omitted for brevity)
}

2.地址

package com.example.jpa.model;

import javax.persistence.Embeddable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Embeddable
public class Address {
    @NotNull
    @Size(max = 100)
    private String addressLine1;

    @NotNull
    @Size(max = 100)
    private String addressLine2;

    @NotNull
    @Size(max = 100)
    private String city;

    @NotNull
    @Size(max = 100)
    private String state;

    @NotNull
    @Size(max = 100)
    private String country;

    @NotNull
    @Size(max = 6)
    private String zipCode;


    public Address() {

    }

    public Address(String addressLine1, String addressLine2, String city, 
                   String state, String country, String zipCode) {
        this.addressLine1 = addressLine1;
        this.addressLine2 = addressLine2;
        this.city = city;
        this.state = state;
        this.country = country;
        this.zipCode = zipCode;
    }

    // Getters and Setters (Omitted for brevity)
}

用户实体

现在让我们创建将嵌入 NameAddress 类型的 User 模型 -

package com.example.jpa.model;

import org.hibernate.validator.constraints.Email;
import javax.persistence.*;
import javax.validation.constraints.NotNull;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private Name name;

    @NotNull
    @Email
    @Column(unique = true)
    private String email;

    @Embedded
    @AttributeOverrides(value = {
        @AttributeOverride(name = "addressLine1", column = @Column(name = "house_number")),
        @AttributeOverride(name = "addressLine2", column = @Column(name = "street"))
    })
    private Address address;

    public User() {

    }

    public User(Name name, String email, Address address) {
        this.name = name;
        this.email = email;
        this.address = address;
    }

    // Getters and Setters (Omitted for brevity)
}

我们使用 JPA 的 @Embedded 注释在模型类中嵌入一个类型。注意 @AttributeOverrides@AttributeOverride 注释的使用。这些注释可帮助您自定义/覆盖可嵌入类型的字段。

创建存储库

接下来,让我们创建用于从数据库访问用户数据的存储库。首先在com.example.jpa包中创建一个名为repository的包,然后在repository包中添加如下接口——

package com.example.jpa.repository;

import com.example.jpa.model.Name;
import com.example.jpa.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}

我们从 Spring Data JPA 的 JpaRepository 接口扩展了 UserRepositoryJpaRepository 接口包含实体上所有 CRUD 操作的方法。 Spring Boot 在运行时自动注入一个名为 SimpleJpaRepository 的接口实现。

这有助于我们在实体上执行所有 CRUD 操作,而无需自己实现任何东西。

测试嵌入式和嵌入式设置

最后,让我们编写一些代码来测试我们的 Embeddable 类型设置。打开主类 JpaEmbeddableDemoApplication.java 并将其替换为以下代码 -

package com.example.jpa;

import com.example.jpa.model.Address;
import com.example.jpa.model.Name;
import com.example.jpa.model.User;
import com.example.jpa.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JpaEmbeddableDemoApplication implements CommandLineRunner {

    @Autowired
    private UserRepository userRepository;

    public static void main(String[] args) {
        SpringApplication.run(JpaEmbeddableDemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        // Cleanup the users table
        userRepository.deleteAllInBatch();

        // Insert a new user in the database
        Name name = new Name("Rajeev", "Kumar", "Singh");
        Address address = new Address("747", "Golf View Road", "Bangalore", "Karnataka", "India", "560008");
        User user = new User(name, "rajeev@callicoder.com", address);

        userRepository.save(user);
    }
}

在上面的类中,我实现了 Spring 的 CommandLineRunner 接口并编写了代码来测试我们在 run() 方法中的设置。 run() 方法在应用程序启动成功时被调用。

run() 方法中,我们首先清理用户的表,然后在数据库中插入一个新的用户。

运行应用程序

您可以通过从项目的根目录键入以下命令来运行该应用程序 -

mvn spring-boot:run

查看日志以查看 hibernate 已执行的 SQL 语句 -

org.hibernate.SQL : delete from users
org.hibernate.SQL : insert into users (city, country, house_number, state, street, zip_code, email, first_name, last_name, middle_name) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

如果您在运行应用程序后检查 MySQL 中的 users 表,您将找到我们使用 hibernate 插入的条目。

mysql> select * from users;
+----+------------+-------------+-----------+-----------------------+--------------+----------------+-----------+-----------+---------+----------+
| id | first_name | middle_name | last_name | email                 | house_number | street         | city      | state     | country | zip_code |
+----+------------+-------------+-----------+-----------------------+--------------+----------------+-----------+-----------+---------+----------+
|  1 | Rajeev     | Kumar       | Singh     | rajeev@callicoder.com | 747          | Golf View Road | Bangalore | Karnataka | India   | 560008   |
+----+------------+-------------+-----------+-----------------------+--------------+----------------+-----------+-----------+---------+----------+
1 row in set (0.00 sec)

注意表结构是如何线性的,但在应用程序级别,我们将 NameAddress 细节抽象为单独的类。

使用可嵌入类型的 Spring Data JPA 查询方法

Spring Data JPA 有一个很好的特性,它可以根据我们在存储库中指定的方法名称生成查询。

例如,要查找带有电子邮件的用户,您可以在 UserRepository 和 Sp 中添加如下查询方法 JPA 将自动从中生成查询,而无需您实施任何操作 -

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Find user by email
    User findByEmail(String email);
}

您可以查看官方 Spring Data JPA 的文档以了解有关 Query 方法的更多信息。

所以想法是您使用实体类的字段名称编写查询方法。 当您使用可嵌入类型并且需要使用可嵌入类型的字段查询实体时,您可以指定这样的查询方法 -

// Find users by firstName
List<User> findByNameFirstName(String firstName);

// Find users by lastName
List<User> findByNameLastName(String lastName);

// Find users by country
List<User> findByAddressCountry(String country);

相关文章