如何在Spring Boot Services中使用多态性



我目前正在处理一个Spring Boot项目,我想加快编写服务/数据层样板代码的过程(每个实体有一个服务和一个存储库(CrudRepository(,每个实体都有基本相同的方法(。

到目前为止,我在几个实体中使用TABLE_PER_CLASS继承(例如:WarehouseOfficeLocation的子类(为所有位置定义公共属性的抽象类(。

我想定义一个存储库和一个服务来管理Location及其子类型,这样我就可以在我的控制层中做一些类似的事情:

@Autowired
LocationsService locationsService;
Warehouse cityWarehouse = new Warehouse();
Office centralOffice = new Office();
locationsService.addNewLocation(cityWarehouse);
locationsService.addNewLocation(centralOffice);

我知道我可以使用方法重载,但我真的希望避免在这种情况下重复相同的代码。

我也尝试过使用参数多态性:

@Service
public class LocationsService {
@Autowired
LocationsRepository locationsRepository;
public void addNewLocation(Location location) {
locationsRepository.save(location);
}
}

不幸的是,这不起作用,因为Spring无法判断我是想保存Location还是Warehouse对象:

nested exception is org.springframework.orm.jpa.JpaObjectRetrievalFailureException: 
Unable to find com.test.springboot.entities.locations.Location with id 55db6993-8a58-4e3a-a6ab-d60d93ab6182; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.test.springboot.entities.locations.Location with id 55db6993-8a58-4e3a-a6ab-d60d93ab6182

我需要使用具体的Location对象,所以使用@MappedSuperclass不是一个选项。

我有什么东西不见了吗?有可能实现我想要的吗?

请注意,我是Spring Boot的新手,所以可能有一些明显的事情我还不知道。

在简要阅读了JPA规范后,由于一些评论,我使它能够正常工作。

因为我想使用我的超类作为实体,所以我最终使用了SINGLE_TABLE继承。

例如,这是Location实体:

@Entity
@Data
@Accessors(chain = true)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "location_type")
public class Location {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// Skipped 
}

这里的关键是在父类中使用@DiscriminatorColumnSINGLE_TABLE继承,并在相应的子类中添加@DiscriminatorValue

@Entity
@Data @Accessors(chain = true)
@DiscriminatorValue("warehouse_location")
public class Warehouse extends Location {
@JoinColumn(name = "INTERNAL_ROUTE_ID")
@OneToOne(orphanRemoval = true)
private Route internalRoute;
@JoinColumn(name = "EXTERNAL_WAREHOUSE_ID")
@OneToMany(orphanRemoval = true, fetch = FetchType.EAGER)
private List<Warehouse> externalWarehouses;
}

这样,我可以将LocationsRepository定义为:

public interface LocationsRepository extends CrudRepository<Location, Long> {
Warehouse findByCityIgnoreCase(String city);
}

还要注意,只要显式指定了子类的返回类型,就可以在这里定义子类特定的方法(否则该方法将返回ALL Locations,而不仅仅是Warehouse(。

最后,在服务层中,我可以通过将存储库调用的结果下转换到适当的子类中,使相关方法返回任何实体

最新更新