当前位置: 首页 > 面试题库 >

Spring JPA / Hibernate事务强制插入而不是更新

皇甫飞宇
2023-03-14
问题内容

编辑。在扩展基本存储库类并添加insert方法的同时,一个更优雅的解决方案似乎是在实体中实现Persistable。查看 可能的解决方案2

我正在使用springframework.data.jpaHibernate作为ORM 创建服务JpaTransactionManager

遵循本教程的基础。 http://www.petrikainulainen.net/spring-data-jpa-
tutorial/

我的实体存储库扩展 org.springframework.data.repository.CrudRepository

我正在使用使用有意义的主键而不是自动生成的ID的旧数据库

这种情况不应该真的发生,但是由于测试中的错误,我碰到了这种情况。订单表具有有意义的键OrderNumber(M000001等)。主键值在代码中生成,并在保存之前分配给对象。旧版数据库不使用自动生成的ID密钥。

我有一个正在创建新订单的交易。由于存在错误,我的代码生成了数据库中已存在的订单号(M000001)

执行repository.save会导致现有订单被更新。我想要的是强制插入并由于重复的主键而使事务失败。

我可以在每个存储库中创建一个Insert方法,该方法在执行保存之前执行查找,如果该行存在则失败。一些实体具有带有OrderLinePK对象的复合主键,所以我不能使用基础spring
FindOne(ID id)方法

在春季JPA中,有没有一种干净的方法可以做到这一点?

我以前使用spring /
Hibernate和我自己的基本存储库创建了一个不带jpa存储库的测试服务。我实现了一个Insert方法和Save方法,如下所示。

这似乎工作正常。使用的save方法getSession().saveOrUpdate给出了我现在正在体验的内容,并且正在更新现有行。

使用插入方法getSession().save失败,并需要重复的主键。

@Override
public Order save(Order bean) {

    getSession().saveOrUpdate(bean);
    return bean;
}

@Override
public Order insert(Order bean) {
    getSession().save(bean);
    return bean;
}

可能的解决方案1

基于Spring文档的1.3.2章,此处 http://docs.spring.io/spring-
data/jpa/docs/1.4.1.RELEASE/reference/html/repositories.html

可能不是最有效的,因为我们正在执行附加检索以在插入之前检查行的存在,但这是主键。

扩展存储库以添加除保存之外的insert方法。这是第一次切。

我必须将密钥传递给插入以及实体。 我可以避免吗?

我实际上不希望返回数据。 这个entitymanager没有一个exist方法(是否存在,只是做一个count(*)检查一行是否存在?)

import java.io.Serializable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

/**
 *
 * @author Martins
 */
@NoRepositoryBean
public interface IBaseRepository <T, ID extends Serializable> extends JpaRepository<T, ID> {

    void insert(T entity, ID id);

}

实现:定制存储库基类。注意:如果我沿这条路线走,将会创建一个自定义的异常类型。

import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;


public class BaseRepositoryImpl<T, ID extends Serializable> 
        extends SimpleJpaRepository<T, ID> implements IBaseRepository<T, ID> {

    private final EntityManager entityManager;

    public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        this.entityManager = em;
    }


    public BaseRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super (entityInformation, entityManager);
        this.entityManager = entityManager;

    }

    @Transactional
    public void insert(T entity, ID id) {

        T exists = entityManager.find(this.getDomainClass(),id);

        if (exists == null) {
          entityManager.persist(entity);
        }
        else 
          throw(new IllegalStateException("duplicate"));
    }

}

定制存储库工厂bean

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;

/**
 * This factory bean replaces the default implementation of the repository interface 
 */
public class BaseRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
  extends JpaRepositoryFactoryBean<R, T, I> {

  protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {

    return new BaseRepositoryFactory(entityManager);
  }

  private static class BaseRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {

    private EntityManager entityManager;

    public BaseRepositoryFactory(EntityManager entityManager) {
      super(entityManager);

      this.entityManager = entityManager;
    }

    protected Object getTargetRepository(RepositoryMetadata metadata) {

      return new BaseRepositoryImpl<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 IBaseRepository.class;
    }
  }
}

最后,在配置中连接自定义存储库基类

// Define this class as a Spring configuration class
@Configuration

// Enable Spring/jpa transaction management.
@EnableTransactionManagement

@EnableJpaRepositories(basePackages = {"com.savant.test.spring.donorservicejpa.dao.repository"}, 
        repositoryBaseClass = com.savant.test.spring.donorservicejpa.dao.repository.BaseRepositoryImpl.class)

可能的解决方案2

实现Persistable实体的接口并覆盖isNew()

基础实体类,用于管理用于设置持久化标志的回调方法

import java.io.Serializable;
import javax.persistence.MappedSuperclass;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostUpdate;


@MappedSuperclass
public abstract class BaseEntity implements Serializable{

    protected transient boolean persisted;


    @PostLoad
    public void postLoad() {
        this.persisted = true;
    }

    @PostUpdate
    public void postUpdate() {
        this.persisted = true;
    }

    @PostPersist
    public void postPersist() {
        this.persisted = true;
    }

}

然后,每个实体都必须实现isNew()getID()

导入java.io.Serializable; 导入javax.persistence.Column;
导入javax.persistence.EmbeddedId; 导入javax.persistence.Entity;
导入javax.persistence.Table; 导入javax.xml.bind.annotation.XmlRootElement;
导入org.springframework.data.domain.Persistable;

@Entity
@Table(name = "MTHSEQ")
@XmlRootElement

public class Sequence extends BaseEntity implements Serializable, Persistable<SequencePK> {

    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected SequencePK sequencePK;
    @Column(name = "NEXTSEQ")
    private Integer nextseq;

    public Sequence() {
    }


    @Override
    public boolean isNew() {
        return !persisted;
    }

    @Override
    public SequencePK getId() {
        return this.sequencePK;
    }



    public Sequence(SequencePK sequencePK) {
        this.sequencePK = sequencePK;
    }

    public Sequence(String mthkey, Character centre) {
        this.sequencePK = new SequencePK(mthkey, centre);
    }

    public SequencePK getSequencePK() {
        return sequencePK;
    }

    public void setSequencePK(SequencePK sequencePK) {
        this.sequencePK = sequencePK;
    }

    public Integer getNextseq() {
        return nextseq;
    }

    public void setNextseq(Integer nextseq) {
        this.nextseq = nextseq;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (sequencePK != null ? sequencePK.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Sequence)) {
            return false;
        }
        Sequence other = (Sequence) object;
        if ((this.sequencePK == null && other.sequencePK != null) || (this.sequencePK != null && !this.sequencePK.equals(other.sequencePK))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.savant.test.spring.donorservice.core.entity.Sequence[ sequencePK=" + sequencePK + " ]";
    }



}

最好抽象出isNew(),但是我认为我不能。getId不能作为实体具有不同的ID,因为您可以看到此ID具有复合PK。


问题答案:

我以前从未做过,但是稍加修改可能会完成任务。

Persistable实体有一个接口。它具有一种方法boolean isNew(),在实施时将用于“评估”实体是否为数据库中的新实体。根据该决定,在您从调用之后,EntityManager应该决定调用该实体.merge().persist()对该实体进行调用。.save()``Repository

这样,如果您实现isNew()始终返回true,.persist()则不应将称为,然后应引发错误。

如我错了请纠正我。不幸的是,我现在无法在实时代码上对其进行测试。

有关的文档 Persistable:[http]( http://docs.spring.io/spring-
data/commons/docs/current/api/org/springframework/data/domain/Persistable.html)
//docs.spring.io/spring-
data/commons/docs/current/api/org/springframework/data/domain/Persistable.html


 类似资料:
  • 问题内容: 我有一个简单的测试,尝试在其中更新对象,但是合并似乎在执行插入操作而不是更新操作。 } 这是我的更新方法 更新代码 现在,我收到以下错误无法提交JPA事务;嵌套的异常是javax.persistence.RollbackException:标记为rollbackOnly的事务 问题答案: 我有一个版本列,当种子数据插入数据库时​​未设置该列。因此,所有有关更新和删除的问题

  • 问题内容: 我正在使用Oracle 11GR2,并且当varchar2字段为空时,在空字段上执行a 将显示在我的Eclipse控制台上。如何让它显示空字符串呢? 问题答案: 你的getter方法:

  • 嗨,我是Spring Data JPA的新手,我想知道即使我将Id传递给实体,Spring data jpa也是插入而不是合并的。我想当我实现持久接口并实现这两个方法时: 它将自动合并而不是持久化。 我有一个名为User like的实体类: 再上一节课 我有一个名为UserRepostory的自定义存储库,它扩展了JpaReopistory。当我看到实现演示SpringDataJpa使用以上两种方

  • 我有一个模型 视图定位对象: 然后它会做出一些改变(或者可能不会) 然后它尝试保存任何更改: 这里是Django尝试插入另一行而不是更新的地方,导致了一个DB错误 解决方案中有一种类似的问题: 一旦我将primary_key字段设置为自动字段,问题就消失了。 但是,我的主键已经是一个自动字段。 所以我用调试器逐步浏览了django代码,并在文件中资助了这个: 似乎如果没有更改(因此更新不做任何事情

  • 问题内容: 我有一个产品对象,它属于某些类别,即经典的多对一关系。 我想插入和更新产品而不预先选择类别。像这样: 要么 是否可以在不选择类别的情况下进行更新和插入?我不想为此使用HQL或直接查询。 问题答案: session.load()专门用于此类情况。以下: 不会打数据库。但是,如果没有提供给定ID的类别,它将在稍后阶段(刷新期间或多或少)引发异常。 使用速度快且没有副作用(级联等)。

  • 问题内容: 情况 : 我有一个具有java.util.Date类型变量的可持久类: DB中的对应表: 然后,将我的Period对象保存到DB: 之后,如果我尝试从数据库中提取对象,则使用时间戳记类型的变量startDate返回该对象-我尝试使用equals(…)的所有位置均返回false。 问题 :是否有任何方法可以强制Hibernate以java.util.Date类型的对象(而不是Timest