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

时间:2014-10-24 07:50:29

标签: java spring spring-data spring-data-jpa

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

我的问题是,在我写这篇文章之前,我使用了UserRepositoryCustom的单独findByLogin接口的模式,然后是另一个类UserRepositoryImpl,在那个类中,我将拥有{ {1}}获取当前@PersistenceContext

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

EntityManager

3 个答案:

答案 0 :(得分:15)

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

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

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

答案 1 :(得分:3)

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

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

public interface UserRepository extends JpaRepository<User, UUID> {

    Optional<User> findByLoginIgnoreCase(String login) {
}

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

可以找到样本here

答案 2 :(得分: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());
        ...
    }
}