当使用新的 Spring Data Evans 版本时,能够使用 java 8 附带的一些好东西真是太好了。其中之一是接口中的默认实现。下面的存储库使用 QueryDSL 来确保查询类型安全。
我的问题是,在我写这篇文章之前,我使用了
UserRepositoryCustom
的单独 findByLogin
接口的模式,然后使用另一个类 UserRepositoryImpl
,在该类中我将使用 @PersistenceContext
来获取当前 EntityManager
.
当我没有上课时如何获得
EntityManager
?还可能吗?
@Repository
public interface UserRepository extends JpaRepository<User, UUID> {
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<User> 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<User> findByLogin(String login);
}
/**
* Implementation of exactly these methods.
*/
class UserRepositoryImpl extends QueryDslRepositorySupport implements UserRepositoryCustom {
private static final QUser USER = QUser.user;
@Override
public Optional<User> 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<User, Long> { … }
请注意,命名约定在这里很重要(但可以根据需要进行自定义)。通过扩展
QueryDslRepositorySupport
,您可以访问 from(…)
方法,这样您就不必自己与 EntityManager
进行交互。
或者,您可以让
UserRepository
实现 QueryDslPredicateExecutor
并从存储库外部提交谓词,但这会让您最终得到需要使用 Querydsl 的客户端(这可能是不需要的),而且您不会得到Optional
包装类型 OOTB。
您在界面中看不到
EntityManager
,尽管您可以通过查找来解决它。
但是你为什么要这样做呢? Spring Data JPA 已经支持
Optional
返回类型,因此您不需要实现它。 Spring Data 会为你做这件事。
public interface UserRepository extends JpaRepository<User, UUID> {
Optional<User> 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<T, ID extends Serializable> extends JpaRepository<T, ID> {
EntityManager getEntityManager();
}
然后实施
// DomainRepositoryImpl.java
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import javax.persistence.EntityManager;
import java.io.Serializable;
public class DomainRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements DomainRepository<T, ID> {
private EntityManager entityManager;
public DomainRepositoryImpl(Class<T> 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<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new RepositoryBaseFactory(entityManager);
}
private static class RepositoryBaseFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager;
public RepositoryBaseFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new DomainRepositoryImpl<T, I>((Class<T>) 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<User, UUID> {
public default Optional<User> findByLogin(String login) {
JPAQuery query = new JPAQuery(getEntityManager());
...
}
}