Spring Data - 覆盖某些存储库的默认方法

2024-01-24

我只是盯着spring-data and spring-data-rest我真的很想利用这些工具所提供的功能。在大多数情况下,基本功能非常适合我的用例,但在某些情况下,我需要对底层功能进行大量自定义,并有选择地分配一些存储库来继承我所追求的自定义功能。

为了更好地解释这个问题,在spring-data有 2 个可能的接口,您可以从中继承功能,CrudRepository or PagingAndSortingRepository。我想添加第三个叫做让我们说PesimisticRepository

All the PesimisticRepository所做的是以不同的方式处理已删除的@Entity 的概念。 Adeleted实体是一个其deleted财产是NOT NULL。这意味着 @Entity 可以由PesimisticRepository必须有一个deleted财产。

这一切都是可能的,几年前我实际上已经实现了这一点。 (你可以检查一下here https://github.com/existanze/com.existanze.libraries.orm如果你有兴趣的话)

我当前使用 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> {

}

这是我通常做的事情的一部分。如果您需要,我可以创建一个基本应用程序作为示例。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Data - 覆盖某些存储库的默认方法 的相关文章

随机推荐

  • 第一个计时器勾选后鼠标事件不起作用

    我正在使用 powershell 开发一个带有图形界面的小工具 我对这个问题感到疯狂 例如 我在表单上有一个标签 显示 ping 的 实时 状态 同时 如果单击标签 则会显示一条弹出消息 function GoogleStatus Labe
  • 在应用程序中存储短音频文件/流式传输

    我计划制作一个利用 Parse com 后端进行用户身份验证 帖子 关注者 等的应用程序 我希望用户能够将示例音乐文件上传到 Parse 或替代解决方案 然后能够流式传输或让其他人能够播放它 有人告诉我可以使用 PFFile 存储文件 但是
  • 公式不使用 EPPLUS 计算

    我想转让一个Datatable超越并计算总和 为此 我使用 epplus 这是我的代码 Sheet Cells A1 LoadFromDataTable dsExcel Tables 0 true TableStyles Medium9 S
  • 如何在intellij idea中启用从源滚动到外部库?

    我想查看外部库树中反编译类的位置 我怎样才能做到这一点 谢谢 找到了一种获取所显示的反编译类的包的方法 ctrl click在代码中的包上 java 文件中最上面的命令
  • 在 Gtk+ 中堆叠小部件

    Gtk 中有没有一种方法可以将一个小部件堆叠在另一个小部件之上 不包括 GtkFixed GtkFixed 不能很好地工作有两个原因 1 我需要 Z 顺序 2 我需要一个小部件来拉伸和填充提供的空间 我使用 Gtk Fixed 实际上是 g
  • Android 主屏幕小部件动画

    我正在考虑创建一个支持动画的小部件 最好是通过 android view animation 框架 或者通过在后台服务触发的代码中设置远程视图的属性 有没有人对这两种方法有任何经验 我正在尝试的是可行的 还是我正在走入死胡同 实际上可以为
  • MacOS 上的打包电子应用程序不会生成子节点进程

    我创建了一个电子应用程序 当在主进程中运行时 它将生成一个节点子进程 该子进程将提供一些 html 内容 app on ready gt check for port 80 const port 80 find port port then
  • 来自未完全填充的字符数组的字符串

    显然 下面的代码给出了一个相当奇怪的结果 char data new char 5 data 0 a data 1 b data 2 c out println new String data abc 有没有一种方法可以从字符数组创建字符串
  • Android上React Native Axios上传图片返回网络错误

    我尝试使用以下命令将一些数据 包括图像 上传到服务器Axios 它在 iOS 上完美运行 但在 Android 上 它返回Network Error const data new FormData data append tag tag M
  • Rails 收集 JSON 数据并将其渲染到 Highcharts 图表中

    我正在尝试将标签传递到 x 轴上的 Highcharts 柱形图 目前 图表的工作方式是渲染数据 但 x 轴标签显示 0 1 2 3 等 这是没有任何可渲染内容时的默认值 注释 控制器 def dashboard data Note get
  • 从服务帐户获取访问令牌刷新错误:Google API 中的 invalid_grant

    我正在关注这个例子 https code google com p google api python client source browse samples service account tasks py https code goo
  • javax.ws.rs.client.Client如何配置readTimeOut?

    从com sun jersey api client Client to javax ws rs client Client如何配置客户端 FROM import com sun jersey api client Client Clien
  • 反向滚动

    我很难找到我想要完成的任务的解决方案 我正在尝试使用 JS 或其他库 来实现它 以便当用户在鼠标滚轮上向下滚动时 页面滚动的方式与通常相反 基本上 我希望首先看到页面的底部 当用户滚动时 我希望屏幕的顶部进入视图 我能找到的唯一例子是右列h
  • 具有大图像尺寸的 UIImage - 内存问题 - 崩溃

    我想加载并显示图像 jpg png 具有大尺寸 例如1800x1200 或 2000x1800 宽 x 高 如果我在 UIImageView 中显示如此大尺寸的图像 1800x1200 或 2000x1800 它也会消耗大量内存 并且应用程
  • 即使在 Ajax 页面中显式等待后,Selenium 也无法找到元素

    我正在尝试自动更新 Web 应用程序中的字段 因此登录后url不会改变 到目前为止 这是我的代码 from selenium import webdriver from selenium webdriver common keys impo
  • C++ 中的音频操作

    我希望这是发布此内容的正确位置 并且有人可以提供帮助 我是一名音乐技术学生 最近开始学习 C 因为了解一门编程语言对我的职业生涯有很大帮助 尤其是这种语言 因为它用于视频游戏行业 无论如何 进入主题 我想要创建一个程序 用 C 编写 让用户
  • 在 Python 中检查非常大的数字的素性

    检查给定的大数是否为素数的最快方法是什么 我说的是大小约为 10 32 的数字 我已经尝试过该算法 MarcoBonelli 的精彩回答 https stackoverflow com a 27946768 1195131这是 from m
  • 如何同步两个 Subversion 存储库?

    我公司有一家子公司 互联网连接速度很慢 我们的开发人员在与我们的中央交互时遇到了麻烦颠覆 http en wikipedia org wiki Subversion 28software 29服务器 是否可以为它们配置从属 镜像 他们将与服
  • 如何生成杠杆统计数据?

    我知道如何使用 leverPlot 生成图 但我找不到一种方法来为每个观察生成杠杆统计数据 就像 megastat 输出中一样 我认为您正在寻找帽子的价值 Use hatvalues fit 经验法则是检查任何比平均帽子值大 2 3 倍的观
  • Spring Data - 覆盖某些存储库的默认方法

    我只是盯着spring data and spring data rest我真的很想利用这些工具所提供的功能 在大多数情况下 基本功能非常适合我的用例 但在某些情况下 我需要对底层功能进行大量自定义 并有选择地分配一些存储库来继承我所追求的