我应该使用Java 8默认方法来手动实现Spring Data存储库方法吗?

当使用新的Spring Data Evans版本时,能够使用java 8附带的一些好东西是很好的。其中一个是接口中的默认实现。 下面的存储库使用QueryDSL使查询类型安全。

我的问题是,在我写这篇文章之前,我使用了一个单独的UserRepositoryCustom接口的模式用于findByLogin ,然后是另一个类UserRepositoryImpl ,在该类中,我将使用@PersistenceContext来获取当前的EntityManager

如果没有课程,如何获得EntityManager ? 它甚至可能吗?

 @Repository public interface UserRepository extends JpaRepository { final QUser qUser = QUser.user; // How do I get the entityManager since this is a interface, i cannot have any variables? //@PersistenceContext //EntityManager entityManager; public default Optional findByLogin(String login) { JPAQuery query = new JPAQuery(entityManager); User user = query .from(qUser) .where( qUser.deleter.isNull(), qUser.locked.isFalse(), qUser.login.equalsIgnoreCase(login) ) .singleResult(qUser); return Optional.ofNullable(user); } } 

默认方法只应用于委托对其他存储库方法的调用。 默认方法 – 根据定义 – 无法访问实例的任何状态(因为接口没有)。 它们只能委托给其他接口方法或调用其他类的静态接口方法。

实际上,使用参考文档中描述的自定义实现是正确的方法。 这是参考的简短版本(如果其他人也想知道):

 /** * Interface for methods you want to implement manually. */ interface UserRepositoryCustom { Optional findByLogin(String login); } /** * Implementation of exactly these methods. */ class UserRepositoryImpl extends QueryDslRepositorySupport implements UserRepositoryCustom { private static final QUser USER = QUser.user; @Override public Optional findByLogin(String login) { return Optional.ofNullable( from(USER). where( USER.deleter.isNull(), USER.locked.isFalse(), USER.login.equalsIgnoreCase(login)). singleResult(USER)); } } /** * The main repository interface extending the custom one so that the manually * implemented methods get "pulled" into the API. */ public interface UserRepository extends UserRepositoryCustom, CrudRepository { … } 

请注意,命名约定在这里很重要(但可以根据需要进行自定义)。 通过扩展QueryDslRepositorySupport您可以访问from(…)方法,这样您就不必自己与EntityManager进行交互。

或者,您可以让UserRepository实现QueryDslPredicateExecutor并从存储库外部传递谓词,但这会让您最终得到需要使用Querydsl的客户端(这可能是不需要的),而且您没有获得Optional包装器类型OOTB。

您没有在界面中获得EntityManager ,尽管您可以通过执行查找来解决它。

但你为什么要这样做呢? Spring Data JPA已经支持Optional返回类型,因此您不需要实现它。 Spring Data会为您完成。

 public interface UserRepository extends JpaRepository { Optional findByLoginIgnoreCase(String login) { } 

上面的代码应该是您所需要的。 如果需要,您甚至可以使用@Query指定查询。

可在此处找到样品。

我最终做的是创建一个具有getEntityManager()的存储库库

但是让基类使用spring boot并不是一件容易的事

 // DomainRepository.java import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.NoRepositoryBean; import javax.persistence.EntityManager; import java.io.Serializable; @NoRepositoryBean public interface DomainRepository extends JpaRepository { EntityManager getEntityManager(); } 

然后执行

 // DomainRepositoryImpl.java import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import javax.persistence.EntityManager; import java.io.Serializable; public class DomainRepositoryImpl extends SimpleJpaRepository implements DomainRepository { private EntityManager entityManager; public DomainRepositoryImpl(Class domainClass, EntityManager entityManager) { super(domainClass, entityManager); this.entityManager = entityManager; } public EntityManager getEntityManager() { return entityManager; } } 

但是Spring需要知道如何创建域存储库,因此我们需要创建一个工厂。

 // DomainRepositoryFactoryBean.java import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import javax.persistence.EntityManager; import java.io.Serializable; public class DomainRepositoryFactoryBean, T, I extends Serializable> extends JpaRepositoryFactoryBean { protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { return new RepositoryBaseFactory(entityManager); } private static class RepositoryBaseFactory extends JpaRepositoryFactory { private EntityManager entityManager; public RepositoryBaseFactory(EntityManager entityManager) { super(entityManager); this.entityManager = entityManager; } protected Object getTargetRepository(RepositoryMetadata metadata) { return new DomainRepositoryImpl((Class) metadata.getDomainType(), entityManager); } protected Class getRepositoryBaseClass(RepositoryMetadata metadata) { // The RepositoryMetadata can be safely ignored, it is used by the JpaRepositoryFactory //to check for QueryDslJpaRepository's which is out of scope. return DomainRepository.class; } } } 

然后在创建存储库时告诉spring boot使用此工厂

 // DomainConfig.java @Configuration @EnableJpaRepositories(repositoryFactoryBeanClass = DomainRepositoryFactoryBean.class, basePackages = {"com.mysite.domain"}) @EnableTransactionManagement public class DomainConfig { } 

然后更改UserRepository以使用它。

 @Repository public interface UserRepository extends DomainRepository { public default Optional findByLogin(String login) { JPAQuery query = new JPAQuery(getEntityManager()); ... } }