Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/svg/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何确保事务中发生的数据库更改也被该事务中的所有人看到_Java_Hibernate_Transactions - Fatal编程技术网

Java 如何确保事务中发生的数据库更改也被该事务中的所有人看到

Java 如何确保事务中发生的数据库更改也被该事务中的所有人看到,java,hibernate,transactions,Java,Hibernate,Transactions,我有一个在数据库中保存或更新一组对象的事务。代码如下: @Transactional public void updatePDBEntry(Set<PDBEntry> pdbEntrySet) { for (PDBEntry pdbEntry : pdbEntrySet) { PDBEntry existingEntry = findByAccessionCode(pdbEntry.getAccessionCode()); if (existi

我有一个在数据库中保存或更新一组对象的事务。代码如下:

@Transactional
public void updatePDBEntry(Set<PDBEntry> pdbEntrySet) {
    for (PDBEntry pdbEntry : pdbEntrySet) {
        PDBEntry existingEntry = findByAccessionCode(pdbEntry.getAccessionCode());
        if (existingEntry != null) {
            log.debug("Remove previous version of PDBEntry {}", existingEntry);
            makeTransient(existingEntry);
        }
        makePersistent(pdbEntry);
    }
}
不知怎的,这不起作用,它说它无法插入对象,因为有一个重复的键,尽管我可以在日志中看到它进入makeTransient()。我想这与以下事实有关:所有这些都发生在事务中,因此makeTransient()所做的更改可能不会被makePersistent()方法看到。我可以通过将所有数据从pdbEntry复制到existingEntry,然后执行saveOrUpdate(existingEntry)来解决这个问题,但这是一种肮脏的黑客行为。是否有另一种方法确保makeTransient对makePersistent可见,同时仍将其保留在事务中

编辑:这是我的PDBEntry域模型:

@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = false, of = { "accessionCode", "date" })
@SuppressWarnings("PMD.UnusedPrivateField")
public class PDBEntry extends DomainObject implements Serializable {
    @NaturalId
    @NotEmpty
    @Length(max = 4)
    private String          accessionCode;

    @NaturalId
    @NotNull
    @Temporal(TemporalType.DATE)
    private Date            date;

    private String          header;

    private Boolean         isValidDssp;

    @Temporal(TemporalType.TIMESTAMP)
    private Date            lastUpdated     = new Date(System.currentTimeMillis());

    @OneToOne(mappedBy = "pdbEntry", cascade = CascadeType.ALL)
    private ExpMethod       expMethod;

    @OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Set<Refinement> refinementSet   = new HashSet<Refinement>();

    @OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Set<HetGroup>   hetGroupSet     = new HashSet<HetGroup>();

    @OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Set<Chain>      chainSet        = new HashSet<Chain>();

    @OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Set<Chain>      residueSet      = new HashSet<Chain>();

    public Date getLastUpdated() {
        return new Date(lastUpdated.getTime());
    }

    public void setLastUpdated() throws InvocationTargetException {
        throw new InvocationTargetException(new Throwable());
    }

    public void touch() {
        lastUpdated = new Date(System.currentTimeMillis());
    }

    @Override
    public String toString() {
        return accessionCode;
    }

    public PDBEntry(String accessionCode, Date date) throws NullPointerException {
        if (accessionCode != null && date != null) {
            this.accessionCode = accessionCode;
            this.date = date;
        } else {
            throw new NullPointerException();
        }
    }
}

@MappedSuperclass
public abstract class DomainObject implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long    id;

    public Long getId() {
        return id;
    }

    @Override
    public abstract boolean equals(Object obj);

    @Override
    public abstract int hashCode();

    @Override
    public abstract String toString();
}
特别要注意注释的@naturaid,我对这些进行了注释,因为我的数据中仍然存在一些错误,导致了重复的组,所以我认为我现在应该删除对它们的唯一约束,但我忘记了我正在使用Lombok为我创建equals和hashcode方法,从这行代码中可以看出:

@EqualsAndHashCode(callSuper = false, of = { "pdbEntry", "hetId" })
这导致了重复HetId的错误。我修复了数据并恢复了@naturaid's,现在一切正常


谢谢大家

单笔交易不应该是问题所在

您的建议-将数据从pdbEntry复制到existingEntry是一个更好的解决方案。对于一个人来说,它的数据库密集度要低得多,并且更容易阅读和理解正在发生的事情

另外,如果您是这样做的,那么就不需要对更改的对象执行saveOrUpdate。Hibernate实现了所谓的“透明持久性”。。。这意味着hibernate负责确定需要使用哪些数据操作来将对象与数据库同步。hibernate中的“更新”操作不适用于已持久化的对象。看

在这种情况下,代码如下所示(注意:不需要更新):

public void updatepddbentry(Set pdbEntrySet){
用于(PDBEntry PDBEntry:pdbEntrySet){
PDBEntry existingEntry=findByAccessionCode(PDBEntry.getAccessionCode());
if(existingEntry!=null){
//将相关字段从pdbEntry复制到现有条目-最好使用pdbEntry上的方法
}否则{
makePersistent(pdbEntry);//尽管最好只调用_save(而不是saveOrUpdate),因为您知道它是一个新对象
}
}            
}

单笔交易不应该是问题所在

您的建议-将数据从pdbEntry复制到existingEntry是一个更好的解决方案。对于一个人来说,它的数据库密集度要低得多,并且更容易阅读和理解正在发生的事情

另外,如果您是这样做的,那么就不需要对更改的对象执行saveOrUpdate。Hibernate实现了所谓的“透明持久性”。。。这意味着hibernate负责确定需要使用哪些数据操作来将对象与数据库同步。hibernate中的“更新”操作不适用于已持久化的对象。看

在这种情况下,代码如下所示(注意:不需要更新):

public void updatepddbentry(Set pdbEntrySet){
用于(PDBEntry PDBEntry:pdbEntrySet){
PDBEntry existingEntry=findByAccessionCode(PDBEntry.getAccessionCode());
if(existingEntry!=null){
//将相关字段从pdbEntry复制到现有条目-最好使用pdbEntry上的方法
}否则{
makePersistent(pdbEntry);//尽管最好只调用_save(而不是saveOrUpdate),因为您知道它是一个新对象
}
}            
}
不知怎的,这不起作用,它说它无法插入对象,因为有一个重复的键,尽管我可以在日志中看到它进入makeTransient()

要了解这里发生了什么,首先需要了解Hibernate不会立即将更改写入数据库,更改将在会话中排队,并在
flush
时间写入。因此,即使您看到调用了
makeTransient()
,这并不意味着在调用该方法时,相应的记录实际上已从数据库中删除。SQL delete语句和其他挂起的更改将在
flush
发生时执行(在
commit()
时间显式调用
flush()
,或在执行HQL查询时显式调用)。这在文档中有很好的解释:

有时会话将执行 同步所需的SQL语句 JDBC连接的状态与 保存在内存中的对象的状态。这 该过程称为flush,由 默认设置为以下几点:

  • 在执行某些查询之前
  • org.hibernate.Transaction.commit()
  • Session.flush()
SQL语句是在 顺序如下:

  • 以与相应对象相同的顺序插入所有实体 使用
    session.save()保存。
  • 所有实体更新
  • 所有集合删除
  • 所有集合元素的删除、更新和插入
  • 所有集合插入
  • 以与相应对象相同的顺序删除所有实体 使用会话删除。删除()
  • 例外情况是,对象使用 在以下情况下插入本机ID生成: 他们得救了

    那么,让我们再次看看您的代码:

    01: @Transactional
    02: public void updatePDBEntry(Set<PDBEntry> pdbEntrySet) {
    03:     for (PDBEntry pdbEntry : pdbEntrySet) {
    04:         PDBEntry existingEntry = findByAccessionCode(pdbEntry.getAccessionCode());
    05:         if (existingEntry != null) {
    06:             log.debug("Remove previous version of PDBEntry {}", existingEntry);
    07:             makeTransient(existingEntry);
    08:         }
    09:         makePersistent(pdbEntry);
    10:     }
    11: }
    
    01:@Transactional
    02:
    WARN  2010-10-25 14:28:49,406 main JDBCExceptionReporter:100 - SQL Error: 0, SQLState: 23503
    ERROR 2010-10-25 14:28:49,406 main JDBCExceptionReporter:101 - Batch entry 0 /* delete nl.ru.cmbi.pdbeter.core.model.domain.PDBEntry */ delete from PDBEntry where id='74' was aborted.  Call getNextException to see the cause.
    WARN  2010-10-25 14:28:49,406 main JDBCExceptionReporter:100 - SQL Error: 0, SQLState: 23503
    ERROR 2010-10-25 14:28:49,406 main JDBCExceptionReporter:101 - ERROR: update or delete on table "pdbentry" violates foreign key constraint "fke03a2dc84d44e296" on table "hetgroup"
      Detail: Key (id)=(74) is still referenced from table "hetgroup".
    ERROR 2010-10-25 14:28:49,408 main AbstractFlushingEventListener:324 - Could not synchronize database state with session
    org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
    
    @Entity
    @Data
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    @EqualsAndHashCode(callSuper = false, of = { "pdbEntry", "hetId" })
    @SuppressWarnings("PMD.UnusedPrivateField")
    // extends DomainObject which contains Id, NaturalId is not enough in this case, since duplicate chains still exist
    // in fact this is an error of the PDBFinder and will be fixed in the future
    public class HetGroup extends DomainObject implements Serializable {
        //@NaturalId 
        @NotNull
        @ManyToOne
        private PDBEntry    pdbEntry;
    
        //@NaturalId 
        @NotEmpty
        private String hetId;
    
        private  Integer nAtom;
    
        @Length(max = 8192)
        private String name;
    
        public HetGroup(PDBEntry pdbEntry, String hetId) {
            this.pdbEntry = pdbEntry;
            pdbEntry.getHetGroupSet().add(this);
    
            this.hetId = hetId;
        }
    }
    
    @EqualsAndHashCode(callSuper = false, of = { "pdbEntry", "hetId" })
    
        public void updatePDBEntry(Set<PDBEntry> pdbEntrySet) {
            for (PDBEntry pdbEntry : pdbEntrySet) {
                PDBEntry existingEntry = findByAccessionCode(pdbEntry.getAccessionCode());
                if (existingEntry != null) {
                    // copy relevant fields from pdbEntry to existing Entry - preferably with a method on PDBEntry
                } else {
                    makePersistent(pdbEntry); // although better to just call _save_ (not saveOrUpdate) cause you know it's a new object
                }
            }            
        }
    
    01: @Transactional
    02: public void updatePDBEntry(Set<PDBEntry> pdbEntrySet) {
    03:     for (PDBEntry pdbEntry : pdbEntrySet) {
    04:         PDBEntry existingEntry = findByAccessionCode(pdbEntry.getAccessionCode());
    05:         if (existingEntry != null) {
    06:             log.debug("Remove previous version of PDBEntry {}", existingEntry);
    07:             makeTransient(existingEntry);
    08:         }
    09:         makePersistent(pdbEntry);
    10:     }
    11: }