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

问题描述 投票:0回答:3

当使用新的 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);
    }
}
java spring spring-data spring-data-jpa
3个回答
20
投票

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

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

/**
 * 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。


3
投票

您在界面中看不到

EntityManager
,尽管您可以通过查找来解决它。

但是你为什么要这样做呢? Spring Data JPA 已经支持

Optional
返回类型,因此您不需要实现它。 Spring Data 会为你做这件事。

public interface UserRepository extends JpaRepository<User, UUID> {

    Optional<User> findByLoginIgnoreCase(String login) {
}

上面的代码应该就是您所需要的。如果需要,您甚至可以使用

@Query
指定查询。

可以在此处找到示例。


0
投票

我最终做的是创建一个存储库库,其中有一个

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());
        ...
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.