如何为Spring Data JPA存储库提供多个基类?

时间:2017-02-25 13:42:05

标签: spring spring-data-jpa

我开发了一个应用程序来存储不同的时间序列,例如温度,压力等等。所有这些测量都有一个基类:

@MappedSuperclass
public abstract class Entity {

    @Id
    @Column(name = "f_id")
    private UUID id = UUID.randomUUID();

    @Column(name = "f_location_id")
    private UUID locationId;

    @Column(name = "f_time")
    private long time;

    // getters and setters  
}

我需要一个通用的存储库方法来获得一个测量值,这是一个流行的时间,如:

public T getPrevious(T entity) {
   "SELECT e FROM " + entity.getClass().getSimpleName() + " e WHERE e.time <= :time "
   // only query example here to get you an idea.
}

我关注了Spring文档:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-behaviour-for-all-repositories

但是,该部分告诉我们如何使所有存储库拥有该方法,但我不需要所有(我defenityle不希望我的LocationRepository有那种方法)。我也不想为每个测量实体实现该方法。

我该怎么办?

答案

接受的答案是正确的,但我想发布我的所有代码,因为它显示了如何自动装配EntityManager以及如何在运行时按实体类获取所需的存储库。

public interface TemperatureRepository extends TimeSeriesCrudRepository<Temperature> {}

public class TemperatureRepositoryImpl extends TimeSeriesRepositoryImpl<Temperature> {
    public TemperatureRepositoryImpl(EntityManager entityManager) {
        super(entityManager);
    }
}

// this interface is needed to use any TimeSeries repository in common way
@NoRepositoryBean // annotation important
public interface TimeSeriesCrudRepository<T extends TimeSeriesEntity> extends TimeSeriesRepository<T>, CrudRepository<T, UUID> {}

public interface TimeSeriesRepository<T extends TimeSeriesEntity> {
    void getPrevious(T entity);
}

public class TimeSeriesRepositoryImpl<T extends TimeSeriesEntity>  implements TimeSeriesRepository<T> {

    private final EntityManager entityManager;

    public TimeSeriesRepositoryImpl(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public void getPrevious(T entity) {
        // impl
    }
}

@Service
public class RepositoriesService {

    private Repositories repositories = null;

    @Autowired
    public RepositoriesService(WebApplicationContext appContext) {
        repositories = new Repositories(appContext);
    }

    // this explains why we need TimeSeriesCrudRepository - to return generic interface with both common CRUD and custom meth
    <T extends TimeSeriesEntity> TimeSeriesCrudRepository<T> getRepository(T entity) {
        //noinspection unchecked
        return (TimeSeriesCrudRepository<T>) repositories.getRepositoryFor(entity.getClass());
    }

}

2 个答案:

答案 0 :(得分:1)

您只需为所需的存储库添加自定义实施as explained in the official documentation

我们假设您有这些基本实体:

@MappedSuperclass
public abstract class Entity {
  @Id
  @Column(name = "f_id")
  private UUID id = UUID.randomUUID();
}

@MappedSuperclass
public abstract class TimeSeries extends Entity {
  @JoinColumn(name = "f_location_id")
  @ManyToOne
  private Location location;

  @Column(name = "f_time")
  private long time;
}

以下具体实体:

@Entity
public class Location extends Entity {}

@Entity
public class Temperature extends TimeSeries {}

@Entity
public class Pressure extends TimeSeries {}

以及以下基本存储库接口:

public interface EntityRepository<T exends Entity> extends CrudRepository<T, UUID> {}

public interface TimeSeriesRepository<T extends TimeSeries> extends EntityRepository<T> {}

然后,Location(和类似实体)的存储库接口将简单地定义为:

public interface LocationRepository extends EntityRepository<Location> {}

等等。

可以仅为TimeSeries的后代添加自定义行为:

public interface CustomRepository<T extends TimeSeries> {
  T getPrevious(T entity);
}

public abstract class CustomRepositoryImpl<T extends TimeSeries> implements CustomRepository<T> {
  public T getPrevious(T entity) {
    ...
  }
}

public class TemperatureRepositoryImpl extends CustomRepositoryImpl<Temperature> {}

public interface TemperatureRepository extends TimeSeriesRepository<Temperature>, CustomRepository<Temperature> {}

public class PressureRepositoryImpl extends CustomRepositoryImpl<Pressure> {}

public interface PressureRepository extends TimeSeriesRepository<Pressure>, CustomRepository<Pressure> {}

注意:如果Temperature的存储库名为TemperatureRepository,则必须通过名为TemperatureRepositoryImpl的类实现其他自定义行为,依此类推。这在顶部链接部分的官方文档中进行了解释。这将强制Spring Data基础结构自动发现自定义实现类并在运行时注入自定义行为。

答案 1 :(得分:1)

Spring文档中描述的解决方案仍可用于为您的存储库的子集提供自定义方法如果您可以将存储库放在单独的包中。

您只需要为每个包含<jpa:repositories ... />base-class属性指定com.example.repositories.std

以下告诉spring使用生成在com.example.repositories.special的所有存储库的默认基类和我自己的<jpa:repositories base-package="com.example.repositories.std" entity-manager-factory-ref="emf" /> <jpa:repositories base-package="com.example.repositories.special" base-class="com.example.repositories.special.MyBaseRepositoryImpl" entity-manager-factory-ref="emf" /> 存储库的自定义基类。

from Bio import AlignIO
# reading your sequences:
alignment = AlignIO.read("my_seq.fa", "fasta")

# length of any alignment row is equal, so number of columns is here

cols = len(alignment[0])
# access to the rows and columns is like in the Numpy array
for col in range(cols):  
    if alignment[ : , col][1] == "-":
        print("gap!")

重要提示:“特殊”包不能嵌套在“std”包中,存储库将由默认基类提供。