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...
}
Related
Assume the following model:
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<B> bs;
public B getB(long id) {
for(B b : bs)
if(b.getId() == id) {
return b;
}
}
}
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "a_id")
private A a;
private String someString;
}
I then try to update a property of some entity B :
#Transactional(rollbackFor = Exception.class)
public void doSomeWork() {
A a = aRepository.findById(/* some id */);
a.getB(/* */).setSomeString(/* some string */);
}
When the method returns, I expect the modified entity B to be updated (SQL UPDATE). For some reason, it doesn't happen. I suspect that the framework is only aware about additions/removals to the bs collection, but since every instance in the collection should be a managed entity, the framework should be aware of the changes.
Not sure what I'm missing here.
EDIT:
I created a repository to reproduce the issue:
https://github.com/mikomarrache/hibernate-spring-issue
If you comment lines 25-27 of the MyServiceImpl class, the save in line 22 is performed. However, if you uncomment these lines, it looks like the save in line 22 is ignored but the second save in line 27 is done, and of course it breaks the unique constraint on name. In order to test, simply run the unit test. No need to populate the database, there is an SQL script on the classpath that is executed at startup.
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.
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();
}
Ok so I get this weird issue that I can't fix.
I have 3 entities ( i will write things that only matters imo)
#Data
#Entity // all # are in javax
#Table(name = "a", schema = "pl")
#SequenceGenerator(...)
public class A extends BaseEntity {
#OneToMany(mappedBy = "pk.a")
private Set<ABRel> Bs = new HashSet<ABRel>();
}
#Getter
#Setter
#Entity
#Table(name = "a_b_rel", schema = "pl")
public class ABRel implements IEntity {
#EmbeddedId
private OfferOrderProjectRelId pk;
public OfferOrderProjectRel(B b, A a) {
if (a == null || b == null) {
throw new IllegalArgumentException("B orA equals null");
}
B.addA(this); // this methods just adds ABRel to sets in A and B
A.addB(this);
pk = new ABRelId(b, a);
}
}
#Getter
#Setter
#Embeddable
#EqualsAndHashCode
public class OfferOrderProjectRelId implements Serializable {
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REFRESH })
#JoinColumn(name = "b_id")
private B b;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REFRESH })
#JoinColumn(name = "a_id")
private A a;
public ABRelId(B b, A a) {
setB(b);
setA(a);
}
}
#Data
#Entity (it has javax import)
#Table(name = "b", schema = "pl")
#SequenceGenerator(...)
public class B extends BaseEntity {
#OneToMany(mappedBy = "pk.b")
private Set<ABRel> As = new HashSet<ABRel>();
#NotBlank
#Length(max = 10000)
#Column(name = "type", length = 10000, nullable = false)
private String type;
}
ABRel and ABRelId have private contructor (ABRel() and ABRelId()) but not sure if it matters. Entities are working just fine, so I don't think somethink is wrong with them but meaby I am wrong.
So I'm tryin to add criteria by B.type for my filters. Criteria are made "in" (not sure how to say it :) ) A.class. So here's criteria that I'm tryin to add in my dao ( I can add this to criteria not detached one if someone ask):
DetachedCriteria idCriteria = DetachedCriteria.forClass(A.class, "a");
idCriteria.createAlias("Bs", "btype", JoinType.LEFT_OUTER_JOIN, Restrictions.eq("Bs.pk.B.type", "someType"));
Criteria criteria = getSession().createCriteria(A.class, "a");
criteria.add(Subqueries.propertyIn("id", idCriteria));
What I am tryin to achieve is to get all ABRels that have some specified B.type, then I will have to count it somehow, but this is not my issue atm. I have to use criteria, can't use any HQL. I also read that hibernate has some kind of bug with creating alias beetwen entity and its embedded so I can't make it too (probably thats why I am having to much trouble with it). So any ideas? I'm running out of option so any help would be great!
I almost forget, I'm getting this error
org.hibernate.HibernateException: Unknown entity: null at
org.hibernate.loader.criteria.CriteriaQueryTranslator.getPropertyMapping(CriteriaQueryTranslator.java:638)
at
org.hibernate.loader.criteria.CriteriaQueryTranslator.getType(CriteriaQueryTranslator.java:587)
at
org.hibernate.loader.criteria.CriteriaQueryTranslator.getTypeUsingProjection(CriteriaQueryTranslator.java:569)
at
org.hibernate.loader.criteria.CriteriaQueryTranslator.getTypedValue(CriteriaQueryTranslator.java:627)
at
org.hibernate.criterion.SimpleExpression.getTypedValues(SimpleExpression.java:100)
at
org.hibernate.loader.criteria.CriteriaQueryTranslator.getQueryParameters(CriteriaQueryTranslator.java:335)
at
org.hibernate.criterion.SubqueryExpression.createAndSetInnerQuery(SubqueryExpression.java:151)
at
org.hibernate.criterion.SubqueryExpression.toSqlString(SubqueryExpression.java:68)
UPDATE:
I have added sth like this
criteria.createAlias("As", "oorel", JoinType.LEFT_OUTER_JOIN);
criteria.createAlias("oorel.pk.b", "order", JoinType.LEFT_OUTER_JOIN, Restrictions.eq("type", "order"));
And now I'm getting new error(it's in my native language so i will try to translate it) its postgres and hibernate exception :
Column index out of range: 1, number of columns: 0
Sorry for my bad english and thank you in advance.
This is unfortunetly a hibernate bug which havent been fixed yet. This error appears because hibernate is not able to create alias via entity that contains composite key.
I've got a many-to-one mapping between two entities (A and B, one B can be associated with many As) where I need the ability to have an ID for B on A (A.B_ID) where that particular B entity doesn't exist in the database. Is this possible?
A (simplified) example of our code:
#Entity
#Table(name = "A")
public class A implements java.io.Serializable {
// ...
private B b;
// ...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "B_ID")
public B getB() {
return this.b;
}
// ...
}
#Entity
#Table(name = "B")
public class B implements java.io.Serializable {
// ...
private Set<A> as = new HashSet<A>( 0 );
// ...
#OneToMany( fetch = FetchType.LAZY, mappedBy = "b" )
public Set<A> getAs() {
return this.as;
}
// ...
}
This basic setup ends up with Hibernate trying to save a null value for A.B_ID and that's not allowed:
Caused by: java.sql.BatchUpdateException: ORA-01400: cannot insert NULL into ("MY_SCHEMA"."A"."B_ID")
For clarification, if the entity doesn't already exist, I don't want it to be created. I'd just want A to be inserted with no B in the db. There is no foreign key constraint between the two tables.
I use a #NotFound annotation on the #ManyToOne side to make sure that it won't causes any errors. I haven't tried it yet with a bidirectional relationship though.
Please not that this is a Hibernate specific annotation!
Example:
#NotFound(action=NotFoundAction.IGNORE)