我只是盯着 spring-data 和 spring-data-rest,我真的很想利用这些工具提供的功能。在大多数情况下,基本功能非常适合我的用例,但在某些情况下,我需要对底层功能进行相当多的自定义,并有选择地分配一些存储库来继承我所追求的自定义功能。
为了更好地解释这个问题,在 spring-data 中有 2 个可能的接口(interface),您可以从它们继承功能,CrudRepository 或 PagingAndSortingRepository。我想添加第三个叫做让我们说 PesimisticRepository
PesimisticRepository 所做的只是以不同方式处理已删除的@Entity 的概念。 deleted 实体是其 deleted 属性为 NOT NULL 的实体。这意味着可以由 PesimisticRepository 处理的 @Entity 必须具有 deleted 属性。
这一切都是可能的,我在几年前就已经实现了。 (感兴趣的可以去here看看)
我目前使用 spring-data 的尝试如下:
PagingAndSortingRepository 的扩展
package com.existanze.xxx.datastore.repositories;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.io.Serializable;
@NoRepositoryBean
public interface PesimisticRepository<T,ID extends Serializable> extends PagingAndSortingRepository<T,ID> {
}
为此,我提供了一个扩展 JPARepository
package com.existanze.xxx.datastore.repositories;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.util.Date;
public class JpaPesimisticRepository<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements PesimisticRepository<T,ID> {
private final EntityManager entityManager;
public JpaPesimisticRepository(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.entityManager = em;
}
@Override
@Transactional
public Page<T> findAll(Specification<T> spec, Pageable pageable) {
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
CriteriaQuery<T> criteriaQuery = cb.createQuery(getDomainClass());
Root<T> from = criteriaQuery.from(this.getDomainClass());
Predicate deleted = cb.equal(from.get("deleted"), cb.nullLiteral(Date.class));
criteriaQuery.select(from).where(deleted);
TypedQuery<T> query = this.entityManager.createQuery(criteriaQuery);
return pageable == null ? new PageImpl<T>(query.getResultList()) : readPage(query, pageable, spec);
}
}
然后对于我希望使用悲观方法处理删除的任何 bean,我都这样定义它
package com.existanze.xxx.datastore.repositories;
import com.existanze.xxx.domain.Phone;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface PhoneRepository extends PesimisticRepository<Phone,Integer> {
}
重要的是要解释为什么我希望覆盖这些方法而不是提供自定义方法,例如 findAllButDeleted。原因是因为我还希望悲观删除能够渗透到 spring-data-rest。这样生成的 HTTP 端点将不需要任何形式的自定义。
这似乎只适用于 findAll 方法。但是,对于其余方法,将抛出当前异常。
$ curl http://localhost:8881/phones/23
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 500 </title>
</head>
<body>
<h2>HTTP ERROR: 500</h2>
<p>Problem accessing /phones/23. Reason:
<pre> org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: object is not an instance of declaring class; nested exception is java.lang.IllegalArgumentException: object is not an instance of declaring class</pre></p>
<hr /><i><small>Powered by Jetty://</small></i>
</body>
</html>
此外,我已经阅读了允许您更改所有存储库的默认 JpaRepository 的文档,但同样我需要在每个存储库的基础上执行此操作。
我希望我已经足够描述了。如果需要更好的解释,请在评论部分告诉我。
最佳答案
您可以创建自定义存储库,如下所示:
package com.brunocesar.custom.repository.support;
import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import com.brunocesar.custom.entity.CustomAbstractEntity;
@NoRepositoryBean
public interface CustomGenericRepository<E extends CustomAbstractEntity, PK extends Serializable> extends
JpaRepository<E, PK>, JpaSpecificationExecutor<E> {
EntityManager getEntityManager();
}
package com.brunocesar.custom.repository.support.impl;
import java.io.Serializable;
import java.util.Calendar;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import com.brunocesar.custom.entity.CustomAbstractEntity;
import com.brunocesar.custom.repository.support.CustomGenericRepository;
@Transactional(readOnly = true)
public class CustomGenericRepositoryImpl<E extends CustomAbstractEntity, PK extends Serializable> extends
SimpleJpaRepository<E, PK> implements CustomGenericRepository<E, PK> {
private final EntityManager entityManager;
private final JpaEntityInformation<E, ?> entityInformation;
public CustomGenericRepositoryImpl(final JpaEntityInformation<E, ?> entityInformation,
final EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
this.entityInformation = entityInformation;
}
@Override
@Transactional
public void delete(final E entity) {
Assert.notNull(entity, "Entity object must not be null!");
entity.setChangeDate(Calendar.getInstance().getTime());
entity.setDeleted(true);
}
@Override
public List<E> findAll() {
return super.findAll(this.isRemoved());
}
@Override
public E findOne(final PK pk) {
return this.findOne(this.isRemovedByID(pk));
}
private Specification<E> isRemoved() {
return new Specification<E>() {
@Override
public Predicate toPredicate(final Root<E> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
return cb.isFalse(root.<Boolean> get("deleted"));
}
};
}
private Specification<E> isRemovedByID(final PK pk) {
return new Specification<E>() {
@Override
public Predicate toPredicate(Root<E> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
final Predicate id = cb.equal(root.get("id"), pk);
final Predicate hidden = cb.isFalse(root.<Boolean> get("deleted"));
return cb.and(id, hidden);
}
};
}
@Override
public EntityManager getEntityManager() {
return this.entityManager;
}
protected JpaEntityInformation<E, ?> getEntityInformation() {
return this.entityInformation;
}
}
您还需要一个自定义工厂 bean 来设置您的自定义存储库。看起来像这样:
package com.brunocesar.custom.repository.support.factory;
import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import com.brunocesar.custom.repository.support.impl.CustomGenericRepositoryImpl;
public class CustomGenericRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable> extends
JpaRepositoryFactoryBean<T, S, ID> {
@Override
protected RepositoryFactorySupport createRepositoryFactory(final EntityManager entityManager) {
return new RepositoryFactory(entityManager);
}
private static class RepositoryFactory extends JpaRepositoryFactory {
public RepositoryFactory(final EntityManager entityManager) {
super(entityManager);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(
final RepositoryMetadata metadata, final EntityManager entityManager) {
final JpaEntityInformation<?, Serializable> entityInformation = this.getEntityInformation(metadata
.getDomainType());
return new CustomGenericRepositoryImpl(entityInformation, entityManager);
}
@Override
protected Class<?> getRepositoryBaseClass(final RepositoryMetadata metadata) {
return CustomGenericRepositoryImpl.class;
}
}
}
最后,应用上下文配置。
常见的存储库配置如下所示:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:jpaProperties-ref="jpaProperties" p:jpaVendorAdapter-ref="jpaVendorAdapter"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" />
<jpa:repositories base-package="com.brunocesar.repository"
transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory" />
和自定义,像这样(您可以使用或不使用分离的 EMF 和事务管理器):
<bean id="entityManagerFactoryCustom" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:jpaProperties-ref="jpaProperties" p:jpaVendorAdapter-ref="jpaVendorAdapter"/>
<bean id="transactionManagerCustom" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactoryCustom" />
<jpa:repositories base-package="com.brunocesar.custom.repository,com.brunocesar.custom.repository.support"
factory-class="com.brunocesar.custom.repository.support.factory.CustomGenericRepositoryFactoryBean"
transaction-manager-ref="transactionManagerCustom" entity-manager-factory-ref="entityManagerFactoryCustom" />
示例 1,使用 JpaRepository:
package com.brunocesar.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.brunocesar.entity.CommonEntity;
@Repository
public interface CommonRepository extends JpaRepository<CommonEntity, Long> {
}
示例 2,使用自定义存储库:
package com.brunocesar.custom.repository;
import org.springframework.stereotype.Repository;
import com.brunocesar.custom.entity.CustomEntity;
import com.brunocesar.custom.repository.support.CustomGenericRepository;
@Repository
public interface CustomRepository extends CustomGenericRepository<CustomEntity, Long> {
}
这是我经常做的一部分。如果您需要,我可以创建一个基本应用程序作为示例。
关于java - Spring Data - 覆盖某些存储库的默认方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27950246/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案
这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun