Play Ebean List null - java

I have an issue using Ebean to save a list of object.
I have a three class. the last one included two children class.
#Entity
#Table(name="A")
public class A extends Model {
#Id
public String idA;
#OneToMany(cascade=CascadeType.ALL, mappedBy = "currentA")
private List<B> listOfB;
}
The second class B :
#Entity
public class B extends Model {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
public Long idB;
#ManyToOne
#JoinColumn(name = "idA")
private A currentA;
#OneToMany(cascade=CascadeType.ALL, mappedBy = "currentB")
public ArrayList<C> lstOfC;
public B(List<C> lstC) {
this.lstOfC=lstC;
}
}
And the last one :
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorValue("X")
public class C extends Model {
#ManyToOne
#JoinColumn(name = "idB")
private B currentB;
private setcurrentB(int currentB) {
this.currentB=currentB;
}
}
The problem I am facing is that I need to create a list of C object (lstOfC) but I do not know the value of currentB when I put the element in the list.
I need to set (with a setter) this value later then save it to the database.
But when I try that, the list of C object is null from the list of B in A.
ArrayList<C> lstC=new ArrayList<C>();
c1=new C();
c2=new C();
B=new B(lstC);
for (C c: lstC) {
c.setcurrentB(1);
Ebean.save(c);
}

You example doesn't quite make sense where setcurrentB(1) ... takes 1 but expects an instance of B - I presume that is a reference bean.
It seems like you want to temporarily turn off cascade persist and you can do that on the Transaction.
Transaction tranaction = Ebean.beginTransaction();
try {
// turn off persist cascade for this transaction
transaction.setPersistCascade(false);
for (C c: listC) {
}
Ebean.commitTransaction();
} finally {
Ebean.endTransaction();
}

Related

Hibernate not using Id to find entity

Hibernate appears to not be using the Id field for one specific class.
My setup looks like this:
#Data
#MappedSuperclass
public abstract class IdentifiableObject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
#Data
#Entity
#Table(name = "A")
public class A extends IdentifiableObject {
private String field;
#ManyToOne(targetEntity = B.class)
private B b;
}
#Data
#Entity
#Table(name = "B")
public class B extends IdentifiableObject {
private TypeSomethingElse field;
#ManyToOne(targetEntity = C.class)
private C c;
#OneToMany(
cascade = CascadeType.ALL
)
private List<A> as;
}
#Data
#Entity
#Table(name = "C")
public class C extends IdentifiableObject {
#OneToMany(
cascade = CascadeType.ALL
)
private List<B> bs;
}
In my code I save an object C to the database, use the data in the database to perform some calculations, create a jasper report and delete the object C from the database again. When deleting the C object I was getting this error:
org.hibernate.HibernateException: More than one row with the given identifier was found: A(field="something")
This Exception is thrown in the class:
public abstract AbstractEntityLoader {
protected Object load(
SharedSessionContractImplementor session,
Object id,
Object optionalObject,
Serializable optionalId,
LockOptions lockOptions){
// Some code
}
}
When the load method is triggered for the B objects, the id passed to the load method is the value of the field id. Whenever it is triggered for the A object it passes a A object with only the field attribute filled in, Our id is null. I personally would asume the method would use the Id field in both cases but it does not. Anyone knows what's happening here?
JPA-Repositories:
I use auto implemented interfaces for deleting.
public interface CRepository extends IdentifiableObjectRepository<C>, JpaRepository<C, Integer> {
C findById(Integer cId);
}
PS: The #Data anotation is part of Lombok to provide getters and setters and some other useful methods.
PPS: I have been able to get it to work by adding a new delete method to the JpaRepository: 'void deleteById(Integer id)', so it seems there is an issue with the default CRUDRepository delete method. This feels like a work around and I would still like to know what the reason is for this issue.

Hibernate creating wrong entity subtype in relationship

I have a strange issue where hibernate does not create the expected entity type in a many to one relataionship. We have the following entities with subclass hierarchy (simplified):
#Entity
#Table(name = "A")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1)
public abstract class A {
#Id
...
public Long getId() { ... }
...
}
#Entity
#DiscriminatorValue("1")
public class A1 extends A {
...
}
#Entity
#DiscriminatorValue("2")
public class A2 extends A {
...
}
#Entity
#Table(name = "B")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1)
public abstract class B<AClass extends A> {
protected AClass a;
#Id
...
public Long getId() { ... }
...
public abstract AClass getA();
public void setA(AClass a) { ... }
}
#Entity
#DiscriminatorValue("1")
public class B1 extends B<A1> {
...
#Override
#ManyToOne(fetch = EAGER)
#JoinColumn(name = "A_ID")
public A1 getA() { ... }
}
#Entity
#DiscriminatorValue("2")
public class B2 extends B<A2> {
...
#Override
#ManyToOne(fetch = EAGER)
#JoinColumn(name = "A_ID")
public A2 getA() { ... }
}
In persistence.xml both entities are declared in the order
A2
A1
B2
B1
Now I create instances of A1 and B1 in the DB:
A1 a1 = new A1();
entityManager.persist(a1);
B1 b1 = new B1();
b1.setA(a1);
entityManager.persist(b1);
I can see the instances are saved to the DB correctly each have ID 1, DISCRIMINATOR is also 1, A_ID in B is also 1.
When I now try to get the B (in another hibernate session):
B b = entityManager.find(B.class, 1L);
I get the exception:
org.hibernate.PropertyAccessException: Exception occurred inside getter of B
Caused by: java.lang.ClassCastException: A2 cannot be cast to A1
at B1.getA(B1.java:61)
... 108 more
With debugging I found out that hibernate is creating the correct entity of type B1 and creates an incorrect entity of type A2 for the relationship to A. The correct type A1 is created if the order in the persistence.xml is changed. It seems like hibernate doesn't take the DISCRIMINATOR column of A table into account in this case but always creates the first subtype declared in the configuration. How can this be fixed? Is there something wrong with the annotations?
(I also had the concrete implementation of method getA() with its annotations in the supertype B at first, but this leads to similar problems.)
With Hibernate 5.0.2.Final I was able to make your example work using #ManyToOne(..., targetEntity = A.class). I also replaced public abstract AClass getA(); with an ordinary getter.
#Entity
#Table(name = "B")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1)
public abstract class B<AClass extends A> {
private Long id;
private AClass a;
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.EAGER, targetEntity = A.class)
#JoinColumn(name = "A_ID")
public AClass getA() {
return a;
}
public void setA(AClass a) {
this.a = a;
}
}
#Entity
#DiscriminatorValue("1")
public class B1 extends B<A1> {
// no need to override getA()
}
#Entity
#DiscriminatorValue("2")
public class B2 extends B<A2> {
// no need to override getA()
}
I did not find anything about this behavior in the documentation. So I have only my observations:
Without targetEntity = A.class Hibernate didn't even query the DISCRIMINATOR column of table A when eagerly fetching rows from A along with B, like it already made a decision about actual type of A.
When I added targetEntity = A.class, A.DISCRIMINATOR appeared in the queries, and objects were created with the right sub-classes of class A.
You are using the same join column (A_ID) in both B1 and B2 subclasses.
Use different one in each subclass:
#Entity
#DiscriminatorValue("1")
public class B1 extends B<A1> {
#Override
#ManyToOne(fetch = EAGER)
#JoinColumn(name = "A1_ID")
public A1 getA() { ... }
}
#Entity
#DiscriminatorValue("2")
public class B2 extends B<A2> {
#Override
#ManyToOne(fetch = EAGER)
#JoinColumn(name = "A2_ID")
public A2 getA() { ... }
}
Although it may make sense to reuse the column (with different columns one will anyway be null for each record depending on the subclass), it seems that Hibernate uses column names internally to uniquely identify some mapping elements within the same table. That's why it probably ignores the definition of the many-to-one mapping in B1 and uses the one from B2 for it as well (because B2 is defined before B1 in the persistence.xml).
Late to this one but just to add that Hibernate throws the same error (returning wrong subtype) when you name the subclass fields with the same name in classes that have a relationship with them e.g.
#Entity
public abstract class Box {
...
}
#Entity
public class LargeBox extends Box {
...
}
#Entity
public class SmallBox extends Box {
...
}
#Entity
public class A {
#ManyToOne
private LargeBox box;
}
#Entity
public class B {
#ManyToOne
private SmallBox box;
}
The above will throw an error when reading instances of class B out of the database as box gets cast to LargeBox. Updating to:
#Entity
public class A {
#ManyToOne
private LargeBox largeBox;
}
#Entity
public class B {
#ManyToOne
private SmallBox smallBox;
}
...fixed it for me. Note: example is brief, you need to update getter signatures accordingly too.

Hibernate, manyToMany relation with saved and unsaved object

I have an association Many To Many with A and B objects in my code, the B object of the relation is registered in table "B" in the database, where there are two colums, id and name. The thing is I have 38 B objects that are usually used to associate with A, they have an id from 1 to 38 and they can be choosen by the user, they can't be changed but someone can add an object B and it save it in table B with auto incremental id to associate it with A. So the matter is I how can I do to associate A with a saved or an unsaved B object when I save A?
Here is my code:
Class A
#Entity
#Table(name="A", catalog="sist", schema = "")
public class A implements java.io.Serializable {
#Id
#Basic(optional=false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idA;
#ManyToMany (fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name="AB", joinColumns={#JoinColumn(name="idA")}, inverseJoinColumns={#JoinColumn(name="idB")})
private List<B> bs = new ArrayList();
}
Class B:
#Entity
#Table(name="B", catalog="sist", schema = "")
public class B implements java.io.Serializable {
#Id
#Basic(optional=false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idB;
private String nombre;
#ManyToMany(fetch=FetchType.LAZY, mappedBy="bs")
private List<A> as = new ArrayList();
}
Here is how I save A in a DAO class:
public void actualizarMedida(A a) throws HibernateException {
try {
sesion = sessionFactory.openSession();
tx = sesion.beginTransaction();
sesion.persist(a);
tx.commit();
sesion.flush();
} catch (HibernateException he) {
manejaExcepcion(he);
throw he;
} finally {
sesion.close();
}
}
Now I can save new B objects and associate them with A, but i can't associate A with an existing B object. Can someone help me? Thanks in advance.
Annotate List<A> inside B entity with #JoinTable annotation too, also provide getter and setters
#JoinTable(name="AB", joinColumns={#JoinColumn(name="idB")})
private List<A>;
//getter and setters.

JPA Inheritance issue

Working with JPA 1 (hibernate-core version 3.3.0.SP1 and hibernate-entitymanager version 3.4.0.GA) :
I've some entities similar to those defined below, where ChildOne and ChildTwo extends from the Father entity.
#Entity
#Table(name = "TABLE_FATHER")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.INTEGER, name = Father.C_ID_CTG)
public class Father {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "sq")
#Column(name = "ID_PK", nullable = false)
#BusinessId
private Long id;
...
}
#Entity
#Table(name = "TABLE_CHILD_ONE")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue(Categories.ID_CTG_ONE)
public class ChildOne extends Father {
...
}
#Entity
#Table(name = "TABLE_CHILD_TWO")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue(Categories.ID_CTG_TWO)
public class ChildTwo extends Element {
...
}
Let's say I've one entity having a Father element, and another having a collection of father elements. In both cases, should go the children entities.
#Entity
#Table(name = "TABLE_ONE")
public class OneTable {
#JoinColumn(name = "ID_PK", referencedColumnName = "ID_PK", nullable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Father element;
...
}
#Entity
#Table(name = "TABLE_ANOTHER")
public class Another {
#Fetch(FetchMode.JOIN)
#OneToMany(cascade = CascadeType.ALL, mappedBy = "id", fetch = FetchType.LAZY)
private Collection<Father> elementCollection;
...
}
I'm expecting to obtain always the children elements but when I get the element getElement() returns the father element
and, on the other hand, when I get the collection getElementCollection() the children elements are coming.
Apparently, the #JoinColumn is the cause of this behaviour, doing the join with the father table and forgetting the children elements.
The collection is working as expected.
How could I get the children element with a getElement() call? Any ideas or workarround?
Thanks in advance.
The problem is not caused by #JoinColumn.
The reason is Lazy Loading.
I manage to pinpoint your problem in simpler example.
Forgive me for changing convention from Father to Parent.
In the example below, uninitialized Element is type of jpa.inheritance.issue.Parent_$$_javassist_1. It is a Hibernate Proxy - dynamically created subclass of Parent.
You can "unproxy" it by invoking Hibernate proprietary API getHibernateLazyInitializer().getImplementation().
Collection of elementCollection is also Lazy Initialized. The type of the collection is org.hibernate.collection.PersistentBag which is being initilized with correct data at the time of first access.
Collection is initialized all at once.
Please see the test which successfully passed green with your exact version of Hibernate (3.3.0.SP1/3.4.0.GA).
#Test
public void test() {
Child c = new Child();
em.persist(c);
Another a = new Another();
a.setElement(c);
Collection<Parent> col = new ArrayList<Parent>();
col.add(c);
a.setElementCollection(col);
em.persist(a);
c.setAnother(a);
long idx = a.getId();
tx.commit();
// I'm cleaning the cache to be sure that call to a.getElement() will return proxy.
em.clear();
tx = em.getTransaction();
tx.begin();
a = em.find(Another.class, idx);
Assert.assertNotNull(a);
Parent p = a.getElement();
// At this point p is a type of jpa.inheritance.issue.Parent_$$_javassist_1
Assert.assertTrue(p instanceof Parent);
Assert.assertFalse(p instanceof Child);
// At this point a.elements is a not initialized (empty) collection of type org.hibernate.collection.PersistentBag
// When we access this collection for the first time, records are read from the database
Assert.assertEquals(1, a.getElementCollection().size());
if (p instanceof HibernateProxy) {
p =
(Parent) ((HibernateProxy) p).getHibernateLazyInitializer()
.getImplementation();
}
// At this point p is a type of jpa.inheritance.issue.Child
Assert.assertTrue(p instanceof Child);
}
#Entity
public class Another {
#JoinColumn(name = "element_id", referencedColumnName = "id", nullable = false)
#ManyToOne(fetch=FetchType.LAZY)
private Parent element;
public Parent getElement() {
return element;
}
public void setElement(Parent element) {
this.element = element;
}
#OneToMany(cascade = CascadeType.ALL, mappedBy = "another", fetch = FetchType.LAZY)
public Collection<Parent> elements;
public Collection<Parent> getElementCollection() {
return elements;
}
public void setElementCollection(Collection<Parent> elementCollection) {
this.elements = elementCollection;
}
// #Id ...
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Parent {
#ManyToOne
private Another another;
public Another getAnother() {
return another;
}
public void setAnother(Another another) {
this.another = another;
}
// #Id ...
}
#Entity
public class Child extends Parent {
}
You don't need #DiscriminatorColumn nor #DiscriminatorValue because those annotations are needed with InheritanceType.SINGLE_TABLE as an only resort to determine the type.
With InheritanceType.JOINED Hibernate is able to determine polymorphic type by checking if there is a record in both (Parent and Child) tables with the same Id.
You can turn on hibernate logging to see how the query to determine the type looks like. It works like this:
select
another0_.id as id0_1_,
another0_.element_id as element2_0_1_,
parent1_.id as id1_0_,
parent1_1_.name as name2_0_,
case
when parent1_1_.id is not null then 1
when parent1_.id is not null then 0
else -1
end as clazz_0_
from
Another another0_
inner join
Parent parent1_
on another0_.element_id=parent1_.id
left outer join
Child parent1_1_
on parent1_.id=parent1_1_.id
where
another0_.id=?

JPA/Hibernate: bidirectional OneToMany/ManyToOne relation only works unidirectional

I'm currently experiencing problems with my OneToMany/ManyToOne-Mapping. The mapping looks like this:
public class A implements Serializable {
#EmbeddedId
private AId id;
// Other stuff...
}
#Embeddable
public class AId implements Serializable {
#ManyToOne
#JoinColumn(name = "B_ID", nullable = false)
private B b;
// Other stuff...
}
public class B implements Serializable {
#OneToMany(mappedBy = "id.b")
private List<A> as;
// Other stuff...
}
If I try to access object B by using object A everything works just fine, but the inverse direction doesn't work at all. The relationship is always null.
A objectA = findAById(id);
B objectB = objectA.getB(); // OK
// But... for example
objectB.getAs(); // returns null
I wrote a small query to get all the As for an object B using its primary key:
SELECT as FROM B b, IN(b.as) as WHERE b.id = :id
This works perfectly, I get the expected result.
I checked what is persisted in the DB, too, and it's all right. Has anybody a clue why that relationship only works in one direction?
Regards,
Alex
that's because by default #onetomany has lazy fetch. You can fix that using this
fetch = FetchType.EAGER
public class B implements Serializable {
#OneToMany(mappedBy = "id.b", fetch = FetchType.EAGER)
private List<A> as;
// Other stuff...
}

Categories