How can I handle Identity vs. Equality in sleep mode?

Note. I designed this SSSCE to illustrate my problem. The actual problem is much larger (individual "records" have much more fields and much more data, and the XML data file has 30 thousand records.)

Given the following XML fragment:

<?xml version="1.0" encoding="UTF-8"?>
<manufacturers>

    <manufacturer>
        <name>BMW</name>
        <manufacturing-countries>
            <country code="DE" iso="DEU">Germany</country>
        </manufacturing-countries>
        <using-countries>
            <country code="DE" iso="DEU">Germany</country>
            <country code="JP" iso="JPN">Japan</country>
            <country code="US" iso="USA">United States</country>
            <country code="UK" iso="GBR">Germany</country>
        </using-countries>
    </manufacturer>

    <manufacturer>
        <name>Honda</name>
        <manufacturing-countries>
            <country code="JP" iso="JPN">Japan</country>
            <country code="US" iso="USA">United States</country>
        </manufacturing-countries>
        <using-countries>
            <country code="JP" iso="JPN">Japan</country>
            <country code="US" iso="USA">United States</country>
            <country code="UK" iso="GBR">Germany</country>
        </using-countries>
    </manufacturer>

</manufacturers>

I have a JAX-B parser that reads this XML into the following objects (note that the objects are intended for immutability, they use the builder pattern to create real objects):

// @Immutable
public class Country {
    private String code;
    private String name;
    private String isoCode;

    private Country() {
        // private, no arg constructor
    }

    // getters elided

    // builder elided
}    

// @Immutable
public class Manufacturer {

    private String name;
    // list of countries where manufacturer builds thier product(s)
    private List<Country> manufacturingCountries;
    // list of countries who use manufacturer product(s)
    private List<Country> usingCountries;

    private Manufacturer() {
        // private no-arg constructor
    }
    // getters elided

    // builder elided
}

So, in my code, I can easily load the list of manufacturers from an XML file:

List<Manufacturer> manufacturers = ManufacturerReader.readManufacturersFromXml(xmlFilePath);

I need to turn around and put this information in a database. So far I have a Hibernate mapping file:

hibernate.hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field">
    <class name="Country"
           table="COUNTRIES">

        <id
            name="code"
            length="2"
            column="CODE">
            <generator class="assigned"/>
        </id>

        <property
            name="name"
            length="40"
            column="NAME">
        </property>

        <property
            name="isoCode"
            length="3"
            column="ISO_CODE">
        </property>

    </class>

    <class name="Manufacturer"
           table="MANUFACTURERS">

        <id
            type="string"
            column="ID">
            <generator class="uuid"/>
        </id>

        <property
            name="name"
            length="255"
            column="NAME">
        </property>

        <list
            name="manufacturingCountries"
            table="MANUF_MANUF_COUNTRY"
            cascade="save-update">
            <key column="MANUFACTURER_ID"/>
            <list-index column="POSITION"/>
            <many-to-many class="Country" column="COUNTRY_CODE"/>
        </list>

        <list
            name="usingCountries"
            table="MANUF_USING_COUNTRY"
            cascade="save-update">
            <key column="MANUFACTURER_ID"/>
            <list-index column="POSITION"/>
            <many-to-many class="Country" column="COUNTRY_CODE"/>
        </list>       

    </class>
</hibernate-mapping>

, , (, "" , "JP" ..),

Hibernate, NonUniqueObjectException - () .

Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [Country#JP]
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:190)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:143)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:117)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:685)
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:677)
    at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:252)
    at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
    at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:425)
    at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:362)
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:338)
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
    at org.hibernate.engine.Cascade.cascade(Cascade.java:161)
    at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:475)
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:353)
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:143)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
    at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
    at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
    at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:713)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.java:701)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.java:697)
    at Main.main(Main.java:28)

"" XML-, ?

:

  • " ", hibernate mapping file (s).
  • ( API .)
  • XML-/ ( , API).
  • XML- ( , API .)

1:

, :

import java.util.List;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class Main {
    public static void main(String[] args) {

        List<Manufacturer> manuf = ManufacturerXmlReader.readManufacturersFromXml("manufacturers.xml");

        Session session = HibernateUtil.getSessionFactory().openSession();

        Transaction tx = session.beginTransaction();

        for (Manufacturer m : manuf) {
            session.save(m);
        }

        tx.commit();
        session.close();
        HibernateUtil.shutdown();
    }
}

2:

session.save() session.merge()

Exception in thread "main" org.hibernate.HibernateException: The class has no identifier property: Manufacturer
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:220)
    at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:3876)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:233)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:84)
    at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:867)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:851)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:855)
    at Main.main(Main.java:16)

3:

Hibernate, name, :

<class name="Manufacturer"
       table="MANUFACTURERS">

    <id
        name="id"
        type="string"
        column="ID">
        <generator class="uuid"/>
    </id>

:

Initial SessionFactory creation failed.org.hibernate.PropertyNotFoundException: field [id] not found on Manufacturer
Exception in thread "main" java.lang.ExceptionInInitializerError
    at HibernateUtil.<clinit>(HibernateUtil.java:21)
    at Main.main(Main.java:11)
Caused by: org.hibernate.PropertyNotFoundException: field [id] not found on Manufacturer
    at org.hibernate.property.DirectPropertyAccessor.getField(DirectPropertyAccessor.java:182)
    at org.hibernate.property.DirectPropertyAccessor.getField(DirectPropertyAccessor.java:174)
    at org.hibernate.property.DirectPropertyAccessor.getGetter(DirectPropertyAccessor.java:197)
    at org.hibernate.tuple.PropertyFactory.getGetter(PropertyFactory.java:191)
    at org.hibernate.tuple.PropertyFactory.buildIdentifierProperty(PropertyFactory.java:67)
    at org.hibernate.tuple.entity.EntityMetamodel.<init>(EntityMetamodel.java:135)
    at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:485)
    at org.hibernate.persister.entity.SingleTableEntityPersister.<init>(SingleTableEntityPersister.java:133)
    at org.hibernate.persister.PersisterFactory.createClassPersister(PersisterFactory.java:84)
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:286)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872)
    at HibernateUtil.<clinit>(HibernateUtil.java:17)
    ... 1 more

name/field ID,

    <id
        name="name"
        length="255"
        column="NAME">
        <generator class="assigned"/>
    </id>

:

org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [Country#JP]
    at org.hibernate.impl.SessionFactoryImpl$2.handleEntityNotFound(SessionFactoryImpl.java:435)
    at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:233)
    at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:285)
    at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:152)
    at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
    at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:1038)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:630)
    at org.hibernate.type.EntityType.resolve(EntityType.java:438)
    at org.hibernate.type.EntityType.replace(EntityType.java:298)
    at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:508)
    at org.hibernate.type.CollectionType.replace(CollectionType.java:575)
    at org.hibernate.type.AbstractType.replace(AbstractType.java:176)
    at org.hibernate.type.TypeHelper.replaceAssociations(TypeHelper.java:262)
    at org.hibernate.event.def.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:589)
    at org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:389)
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:303)
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:464)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:255)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:84)
    at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:867)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:851)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:855)
    at Main.main(Main.java:16)
Exception in thread "main" org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [Country#JP]
    at org.hibernate.impl.SessionFactoryImpl$2.handleEntityNotFound(SessionFactoryImpl.java:435)
    at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:233)
    at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:285)
    at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:152)
    at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
    at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:1038)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:630)
    at org.hibernate.type.EntityType.resolve(EntityType.java:438)
    at org.hibernate.type.EntityType.replace(EntityType.java:298)
    at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:508)
    at org.hibernate.type.CollectionType.replace(CollectionType.java:575)
    at org.hibernate.type.AbstractType.replace(AbstractType.java:176)
    at org.hibernate.type.TypeHelper.replaceAssociations(TypeHelper.java:262)
    at org.hibernate.event.def.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:589)
    at org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:389)
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:303)
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:464)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:255)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:84)
    at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:867)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:851)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:855)
    at Main.main(Main.java:16)
+4
3

, - , Hibernate . :

  • , .
  • , Hibernate.
  • , -//

, , , , , , merge .

- , , .

0

Hibernate, , , : Hibernate: () ()

( XML), - , ( , Hibernate). , merge ( Hibernate 1).

Hibernate , ; , .

Merge , , Hibernate.

, :

Manufacturer manufacturerDB = session.merge(manufacturerFromXML);

DB , , XML.

, , (int manufacturerXML) countryDB ( Hibernate by session.load(...), , , Hibernate, 1, - , NonUnique...), .

+1

.

...

0

All Articles