【问题标题】:Composite Primary Key leads to org.hibernate.NonUniqueObjectException复合主键导致 org.hibernate.NonUniqueObjectException
【发布时间】:2015-04-30 17:56:34
【问题描述】:

我正在尝试为表 City 使用复合主键,如果它没有分配给 Country,则该表不应该存在(它应该是多对一 识别关系)。

我的问题首先是ChildId(参见嵌套类)在我初始化Child 之后有一个@GeneratedValuenull,如下所示:

List<City> cityList = new ArrayList<>();    
City graz = new City(austria, "Graz");
// Runs without exceptions but graz.getCityId().getId() will be 'null'
cityList.add(graz);
cityList.add(new City(austria, "Wien"));

似乎因为它我得到了一个org.hibernate.NonUniqueObjectException 异常:

完整的堆栈跟踪:

Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.mahlzeit.datamodel.address.City#com.mahlzeit.datamodel.address.City$CityId@8a62f66]
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:138)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:204)
    at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:55)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:189)
    at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:49)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
    at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:642)
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:635)
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:631)
    at com.mahlzeit.datamodel.HibernateTest.addAddressData(HibernateTest.java:63)
    at com.mahlzeit.datamodel.HibernateTest.populateDatabase(HibernateTest.java:35)
    at com.mahlzeit.datamodel.HibernateTest.main(HibernateTest.java:31)

我不知道我是否可以这样做。如果是,我怎样才能做到这一点?

City.java

@Entity
public class City implements Serializable {

    private static final long serialVersionUID = -4374113410767348574L;

    @EmbeddedId
    private CityId cityId;

    private String cityName;

    public City(Country country, String cityName) {
        setCityId(new CityId(country));
        this.cityName = cityName;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String countryName) {
        this.cityName = countryName;
    }

    public CityId getCityId() {
        return cityId;
    }

    public void setCityId(CityId cityId) {
        this.cityId = cityId;
    }

    @Embeddable
    public static class CityId implements Serializable{

        private static final long serialVersionUID = -8561021314776406519L;

        @GeneratedValue
        private Long id;

        private Country country;

        public CityId(Country country) {
            this.setCountry(country);
        }

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public Country getCountry() {
            return country;
        }

        public void setCountry(Country country) {
            this.country = country;
        }       
    }
}

Country.java

@Entity
public class Country implements Serializable {

    private static final long serialVersionUID = -2060021861139912774L;

    @Id 
    @GeneratedValue
    private Long id;

    @Column(unique=true)
    private String countryCode;

    public Country(String country_code) {
        this.countryCode = country_code;
    }

    public String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String countryCode) { 
        this.countryCode = countryCode;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

【问题讨论】:

    标签: hibernate


    【解决方案1】:

    对于City 类,主键是CityId,并且这个类有两个属性——idcountry

    请注意,@GeneratedValue 只有在属性也用@Id 注释时才有用,因此在CityId 类中为id 属性添加GeneratedValue 没有用处,休眠将忽略这一点。你也不能为这个属性添加@Id,因为这个类代表@Embeddable

    现在在您的程序中,您正在像这样设置CityId

    public City(Country country, String cityName) {
        setCityId(new CityId(country));
        this.cityName = cityName;
    }
    

    CityId的构造函数是:

        public CityId(Country country) {
            this.setCountry(country);
        }
    

    所以最后你要创建 2 个具有相同国家/地区的城市:

    City graz = new City(austria, "Graz");
    new City(austria, "Wien");
    

    此外,由于您没有为 CityId 类的 id 设置值,因此它将始终为 null。因此city 对象grazwien 具有相同的主键,因为country 引用和id 的组合(在这种情况下为空)。


    您还需要在实体之间进行适当的映射,请参阅此 SO 帖子以获取类似示例:

    How to Represent Composite keys in Hibernate using Annotations?

    来到CityIdid 属性,您必须明确设置它,否则它将始终为null

    【讨论】:

    • 是的,但是我将如何生成CityId.id?这只是必须@Id 一样自动生成。我该怎么做?我不想明确设置它,因为如果该值仍然可用,我必须自己查找。必须有一种使用自动生成(或自动递增)值的方法..
    • @StefanFalk,我的回答是关于为什么你会遇到这个问题并解决它,你必须明确设置该 ID,自动生成将不起作用。但我看到你已经有一个单独的问题 - stackoverflow.com/questions/29976363/… 和史蒂夫并给出了答案,你尝试过那个选项吗?
    • 不,我不在这台机器上,但我会在几个小时后试一试。我会在这里接受你的回答。如果你愿意,我可以让你知道我是否可以解决 id-problem 以及如何解决。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-09
    • 2011-08-17
    • 2013-10-04
    • 2019-10-09
    • 1970-01-01
    • 2022-11-21
    相关资源
    最近更新 更多