I'm trying to use the Bitemporal framework of Erwin Vervaet to store with Hibernate a temporal collection instead of a temporal property as in his example. (there is a presentation of the framework here)
I'm trying to store a collection of addresses which change over time, i.e. a Person can have multiple addresses at the same time, and this set of addresses can change.
The mapping works, I mean the tables are created in the db, but I get the following runtime error:
java.lang.ClassCastException: com.ervacon.bitemporal.AddressSet cannot be cast to java.util.Collection
I don't understand the error. I understand a cast is being done when Hibernate tries to build the addresses
bag of Person
, but why does it get a ClassCastException
? If I comment out the mapping of value
in the entity-name="AddressSet"
mapping, I have no error, but the addresses aren't saved. So the problem is in that mapping I believe.
I also don't understand if what I'm trying to achieve can be done with this framework.
Can you help me?
How I modified Vervaet's example: I added the AddressSet class and modified the Hibernate mapping, the Person and test class
The classes involved are the following:
Person
/*
* (c) Copyright Ervacon 2016.
* All Rights Reserved.
*/
package com.ervacon.bitemporal;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedList;
public class Person implements Serializable {
private Long id;
private String name;
private Collection<BitemporalWrapper<AddressSet>> addresses = new LinkedList<>();
private Collection<BitemporalWrapper<Boolean>> alive = new LinkedList<>();
/**
* For Hibernate.
*/
@SuppressWarnings("unused")
private Person() {
}
public Person(String name) {
if (name == null) {
throw new IllegalArgumentException("Name is required");
}
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public WrappedBitemporalProperty<AddressSet> addresses() {
return new WrappedBitemporalProperty<>(addresses);
}
public WrappedBitemporalProperty<Boolean> alive() {
return new WrappedBitemporalProperty<>(alive);
}
@Override
public String toString() {
return getName();
}
}
Address
/*
* (c) Copyright Ervacon 2016.
* All Rights Reserved.
*/
package com.ervacon.bitemporal;
import java.io.Serializable;
public class Address implements Serializable {
private String line1;
private String line2;
private String line3;
private long id;
/**
* For Hibernate.
Address.java*/
@SuppressWarnings("unused")
private Address() {
}
public Address(String line1, String line2, String line3) {
this.line1 = line1;
this.line2 = line2;
this.line3 = line3;
}
public String getLine1() {
return line1;
}
public String getLine2() {
return line2;
}
public String getLine3() {
return line3;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Address) {
Address other = (Address) obj;
return other.line1.equals(this.line1)
&& other.line2.equals(this.line2)
&& other.line3.equals(this.line3);
}
return false;
}
@Override
public int hashCode() {
return this.line1.hashCode() + this.line2.hashCode() + this.line3.hashCode();
}
@Override
public String toString() {
return this.line1 + " " + line2 + " " + line3;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
AddressSet
package com.ervacon.bitemporal;
import java.io.Serializable;
import java.util.Set;
public class AddressSet implements Serializable {
private List<Address> addressSet;
private long id;
private AddressSet() {
}
public AddressSet(List<Address> a) {
this.addressSet = a;
}
public List<Address> getAddressSet() {
return addressSet;
}
public void setAddressSet(List<Address> addressSet) {
this.addressSet = addressSet;
}
}
The mapping is:
<?xml version="1.0"?>
<!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="com.ervacon.bitemporal.BitemporalWrapper" entity-name="AddressSet">
<id name="id" type="long">
<generator class="native"/>
</id>
<bag name="value">
<key column="addressSet_id"/>
<one-to-many class="com.ervacon.bitemporal.Address"/>
</bag>
<property name="validityInterval" type="com.ervacon.bitemporal.support.PersistentInterval">
<column name="validityStart"/>
<column name="validityEnd"/>
</property>
<property name="recordInterval" type="com.ervacon.bitemporal.support.PersistentInterval">
<column name="recordStart"/>
<column name="recordEnd"/>
</property>
</class>
<class name="com.ervacon.bitemporal.Person">
<id name="id" type="long">
<generator class="native"/>
</id>
<property name="name"/>
<!-- <bag name="address" cascade="all-delete-orphan">
<key column="person_id" not-null="true" update="false"/>
<one-to-many entity-name="Address"/>
</bag>
-->
<bag name="addresses" cascade="all-delete-orphan">
<key column="person_id" not-null="true" update="false"/>
<one-to-many entity-name="AddressSet"/>
</bag>
<bag name="alive" cascade="all-delete-orphan">
<key column="person_id" not-null="true" update="false"/>
<one-to-many entity-name="Alive"/>
</bag>
</class>
<class name="com.ervacon.bitemporal.Address">
<id name="id" type="long">
<generator class="native"/>
</id>
<property name="line1"/>
<property name="line2"/>
<property name="line3"/>
</class>
<class name="com.ervacon.bitemporal.BitemporalWrapper" entity-name="Alive">
<id name="id" type="long">
<generator class="native"/>
</id>
<property name="value" type="boolean"/>
<property name="validityInterval" type="com.ervacon.bitemporal.support.PersistentInterval">
<column name="validityStart"/>
<column name="validityEnd"/>
</property>
<property name="recordInterval" type="com.ervacon.bitemporal.support.PersistentInterval">
<column name="recordStart"/>
<column name="recordEnd"/>
</property>
</class>
</hibernate-mapping>
The test
package com.ervacon.bitemporal;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest {
private SessionFactory sessionFactory;
@Before
public void setUp() {
System.err.println("Before");
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//@After
public void tearDown() throws Exception {
sessionFactory.close();
TimeUtils.clearReference();
}
@Test
public void testPersistence() {
System.err.println("Testing");
Session session = sessionFactory.openSession();
session.beginTransaction();
TimeUtils.setReference(TimeUtils.day(4, 4, 1975));
Person johnDoe = new Person("John Doe");
johnDoe.alive().set(
true,
TimeUtils.from(TimeUtils.day(3, 4, 1975)));
AddressSet addressSet = new AddressSet(new ArrayList<>());
List<Address> addressList1 = new ArrayList<>();
addressList1.add(new Address("Address1.1", "", ""));
addressSet.setAddressSet(addressList1);
johnDoe.addresses().set(
addressSet,
TimeUtils.from(TimeUtils.day(3, 4, 1975)));
AddressSet addressSet2 = new AddressSet(new ArrayList<>());
List<Address> addressList2 = new ArrayList<>();
addressList2.add(new Address("Address2.1", "", ""));
addressSet2.setAddressSet(addressList2);
johnDoe.addresses().set(
addressSet2,
TimeUtils.from(TimeUtils.day(3, 4, 1976)));
try {
session.save(johnDoe);
session.getTransaction().commit();
session.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The full exception is
java.lang.ClassCastException: com.ervacon.bitemporal.AddressSet cannot be cast to java.util.Collection
at org.hibernate.type.BagType.wrap(BagType.java:35)
at org.hibernate.event.internal.WrapVisitor.processArrayOrNewCollection(WrapVisitor.java:91)
at org.hibernate.event.internal.WrapVisitor.processCollection(WrapVisitor.java:56)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:104)
at org.hibernate.event.internal.WrapVisitor.processValue(WrapVisitor.java:108)
at org.hibernate.event.internal.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:59)
at org.hibernate.event.internal.AbstractSaveEventListener.visitCollectionsBeforeSave(AbstractSaveEventListener.java:354)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:260)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:113)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:97)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:651)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:643)
at org.hibernate.engine.spi.CascadingActions$5.cascade(CascadingActions.java:218)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:391)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:316)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:155)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:424)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:356)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:319)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:155)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:104)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:445)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:281)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:113)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192)
at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177)
at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:682)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:674)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:669)
at com.ervacon.bitemporal.HibernateTest.testPersistence(HibernateTest.java:60)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
edit: changed Set
to List
in AddressSet
, because after using Tijkijiki's solution I got an error while Hibernate was building the bag. After this the test passed.
But I added two set of AddressSet
to the test (which is now modified since the original posting) and now I get this error, someone can explain me why? The AddressSet objects seem different to me.
ERROR: HHH000346: Error during managed flush [Found shared references to a collection: AddressSet.value.addressSet]
org.hibernate.HibernateException: Found shared references to a collection: AddressSet.value.addressSet
at org.hibernate.engine.internal.Collections.processReachableCollection(Collections.java:182)
at org.hibernate.event.internal.FlushVisitor.processCollection(FlushVisitor.java:42)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:104)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:65)
at org.hibernate.event.internal.AbstractVisitor.processValues(AbstractVisitor.java:44)
at org.hibernate.event.internal.AbstractVisitor.processComponent(AbstractVisitor.java:85)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:110)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:65)
at org.hibernate.event.internal.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:59)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:155)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:216)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:85)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:38)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1295)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:468)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3135)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2352)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:491)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
at com.ervacon.bitemporal.HibernateTest.testPersistence(HibernateTest.java:77)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
component
and cannot use the bag directly as Vervaet did in the example? And, can you try to help me for the error I'm getting now? (see the edit section at the bottom of the post) – Nordgren