我正在开发一个应用程序来跟踪客户帐户。每个客户可以有多个帐户。 挑战在于帐户对象是多态的。 有一个抽象类
Account
有 2-3 个方法,然后目前有 2 个子类型 SavingsAccount
和 CreditAccount
。
我尝试的是让
Account
对象使用 @MappedSuperclass
注释,然后每个具体类照常具有 @Entity
注释。
问题似乎是映射我的客户对象
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="customer_id", referencedColumnName = "id")
List<Account> accounts = new ArrayList<>();
当我使用它时,我在 IDE 中收到来自 Spring 的警告,但在运行时我收到此错误。
com.example.models.Customer.accounts' targets the type 'com.example.models.Account' which is not an '@Entity' type
是否没有办法针对多态对象列表进行 @OneToMany 映射?即使它们存储在同一个表中?
您收到的错误与预期一致。您需要使用
@Inheritance
注释您的基本 Account 类。如果使用单个表,Account 类可能如下所示:
import jakarta.persistence.*;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "account_type")
public abstract class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
// getters and setters
}
你的子类将如下所示
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
@Entity
@DiscriminatorValue("SAVINGS")
public class SavingsAccount extends Account {
}
@Entity
@DiscriminatorValue("CREDIT")
public class CreditAccount extends Account {
}
我用这个客户实现验证了上述代码:
import jakarta.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "customer_id")
private List<Account> accounts = new ArrayList<>();
public void addAccount(Account account) {
account.setCustomer(this);
accounts.add(account);
}
// getters/setters
}
客户存储库
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
}
迁徙路线
CREATE TABLE customer
(
id SERIAL PRIMARY KEY
);
CREATE TABLE account
(
id SERIAL PRIMARY KEY,
account_type VARCHAR(16) NOT NULL,
customer_id INT NOT NULL,
CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customer (id)
);
PostgresTestContainerConfig
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.testcontainers.containers.PostgreSQLContainer;
@TestConfiguration
public class PostgresTestContainerConfig {
@Bean
@ServiceConnection
public PostgreSQLContainer<?> postgreSQLContainer() {
return new PostgreSQLContainer<>("postgres:15-alpine");
}
}
测试
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.context.annotation.Import;
import static org.junit.jupiter.api.Assertions.*;
@DataJpaTest
@AutoConfigureTestDatabase
@Import(no.vicx.database.PostgresTestContainerConfig.class)
class CustomerRepositoryTest {
@Autowired
TestEntityManager entityManager;
@Autowired
CustomerRepository sut;
@Test
void testInserts() {
Customer customer = new Customer();
customer.addAccount(new SavingsAccount());
customer.addAccount(new CreditAccount());
sut.save(customer);
assertEquals(2, getAccountCountInDb());
var customerInDb = entityManager.find(Customer.class, customer.getId());
assertInstanceOf(SavingsAccount.class, customerInDb.getAccounts().getFirst());
assertInstanceOf(CreditAccount.class, customerInDb.getAccounts().getLast());
}
long getAccountCountInDb() {
return (long) entityManager.getEntityManager()
.createQuery("SELECT COUNT(1) FROM Account ")
.getSingleResult();
}
}