JPA save()涉及@UniqueContraint字段

时间:2015-07-05 03:10:55

标签: spring hibernate jpa relational-database unique-constraint

我在Spring项目中使用JPA和Hibernate实现。 实体如下:

@Table(uniqueConstraints={@UniqueConstraint(columnNames = {"channel", "start", "end"})})
@Entity(name = "schedule")
public class Schedule {
    @Id
    @Column(name = "idSched", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long idSched;

    @Column(name = "start", nullable = false)
    private Timestamp start;

    @Column(name = "end", nullable = false)
    private Timestamp end;

    @ManyToOne(targetEntity=Channel.class, fetch=FetchType.LAZY, cascade={CascadeType.ALL})
    @JoinColumn(name="channel")
    private Channel channel;
}



@Table(uniqueConstraints={@UniqueConstraint(columnNames = {"idChBusiness"})})
@Entity(name = "channel")
public class Channel {

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long idChPersistence;

    @JsonProperty
    @Column(name = "idChBusiness", nullable = false, length = 50)
    private String idChBusiness;

    @OneToMany(targetEntity=Schedule.class, mappedBy="channel", fetch=FetchType.LAZY, cascade={CascadeType.ALL}, orphanRemoval=true)
    private List<Schedule> listSchedules;
}

然后我为每个实体都有一个存储库:

public interface ChannelRepository extends JpaRepository<Channel, Long>,
        JpaSpecificationExecutor<Channel>, ChannelRepositoryCustom {
...
}

public interface ScheduleRepository extends JpaRepository<Schedule, Long>,
        JpaSpecificationExecutor<Schedule> {
...         
}

我正在使用JPA Repository的save()方法来插入我的实体。 我的主要目标是在单个save()查询中保存Schedule实体列表。

我认为计划在输入数据中是唯一的。也就是说,没有两个具有相同(通道,开始,结束)值的Schedules,无论如何,我正在使用Schedule实体的@UniqueConstraint阻止它。

但是,在我的输入数据中,有不同的Schedule实体引用具有相同idChBusiness的Channels。我不希望表中有两个具有相同idChBusiness的Channel实体,这就是我尝试使用Channel实体的@UniqueConstraint阻止的。

因此,我可以使用某个频道属性保存第一个日程表,并保存这两个实体。

问题在于,当我尝试保存包含与第一个Schedule实体具有相同idChBusiness的channel属性的第二个Schedule时。然后违反了idChBusiness @UniqueContraint,并且未保存Channel实体,但父节目实体也都没有。 具体来说,我得到的例外情况如下:

  

org.springframework.dao.InvalidDataAccessApiUsageException:已分离   实体传递给持久化:com.persistence.domain.Channel;嵌套   异常是org.hibernate.PersistentObjectException:分离的实体   传递给持久化:com.persistence.domain.Channel

但我认为在这种情况下,概念比具体错误更具相关性。

那么,是否有任何JPA模式可以使这个工作并能够使用单个save()查询保存列表计划实体?即使没有,任何其他建议将不胜感激。

谢谢!

更新1:

我尝试用@Jens Schauder方法解决它。这是代码:

public class DatabaseLoader() {

    ...

    public void doLoad(List<Schedule> lSched) {
        for (Schedule sched: lSched) {
            Channel ch = chServ.findByIdChBusiness(sched.getChannel().getIdChBusiness());
            if (ch != null) { // If a duplicate channel does exist -> Then remove it
                chServ.deleteChannel(ch);
            }
            schedServ.insertSchedule(sched);
        }
    }



@Service
@Transactional
public class ChannelService {

    ...

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void deleteChannel(Channel ch) {
        chRep.delete(ch);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void insertChannel(Channel ch) {
        chRep.save(ch);
    }
}

@Service
@Transactional
public class ScheduleService {

    ...

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void insertSchedule(Schedule sched){
        schedRep.save(sched);
    }
}

这就是结果:

成功插入第一个时间表(及其频道)。接下来,我尝试插入第二个schedue,其channel属性与插入的第一个schedule的channel属性具有相同的idChBusiness。

然后满足if条件,所以我应该删除该频道,以避免在Channel表上出现 @UniqueConstraint 违规。当我这样做时,我刚插入的第一个计划也会被删除,因为 cascade = CascadeType.ALL 属性。

此外,在执行 schedServ.insertSchedule(sched)句子时,我得到以下PersistentObjectException:

  

org.hibernate.PersistentObjectException:传递给的分离实体   坚持:com.persistence.domain.Channel

我使用 CascadeType.PERSIST 代替 CascadeType.ALL 进行了尝试,结果相同。

如果我尝试删除CascadeType属性,则永远不会保存Channel对象,并且我得到以下异常:

  

org.hibernate.TransientPropertyValueException:对象引用一个   未保存的瞬态实例 - 保存之前的瞬态实例   flushing:com.persistence.domain.Schedule.channel - &gt;   com.persistence.domain.Channel

我在这里做错了什么?

再次感谢你!

1 个答案:

答案 0 :(得分:2)

您自己发现的唯一约束使交易无法成功完成。因此,您应该将其视为安全网。它可以防止数据库中的错误数据,但它根本无助于解决问题。

解决方案是首先检查是否存在重复,删除重复项,然后保存数据。

仅在(可能很少见)条件下,在检查重复项之后,在实际存储数据之前,引入了重复项,UniqueConstraint将阻止数据进入数据库。在大多数应用程序中,这种情况由一些通用的&#34; Oops充分处理,有些出错了。请再试一次&#34;异常处理程序。