I am new to Java and Hibernate. I've got problem with composite key. I am trying to do something like that:
#Entity
class A {
#Id
int id;
}
#Entity
class B {
#Id
int id;
}
#Entity
class C {
#EmbeddedId
C_PK c_pk;
}
#Embeddable
class C_PK {
A a;
B b;
}
When I perform
...
session.save(c);
...
Then exception is thrown that type of A and B cannot be inserted into database. Is it possible to somehow tell hibernate to don't save the A object but only the A id? Is my approach absolutely wrong and should I just use primitive data types at C_PK class?
You should put a #ManyToOne (or OneToOne) with join columns on the A and B references in C_PK.
#Embeddable
class C_PK {
#ManyToOne
A a;
#ManyToOne
B b;
}
Related
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'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...
}
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)
I have two entitys, A and B. A has a one to one relationship of B.
class A {
String aValue;
B b;
}
class B {
String bValue;
}
The class B stuff are already pre populated. Now, a user on a website selects what B he wants to submit. On the server we get an Id of what B that was.
The Question:
How do I create a new A, without actually do a DB query asking for the B that has this id. I mean, the A table in database only has a id reference. One should be able to set that Id without fetching the B.
If you are using JPA and Assuming the following:
#Entity
#Table(name="A")
class A {
#Id
#Column(name="id")
private int id;
String aValue;
#OneToOne
B b;
// Getters, setters and other stuff
}
#Entity
#Table(name="B")
class B {
#Id
#Column(name="id")
private int id;
Integer Id;
String bValue;
// Getters, setters and other stuff
}
If you now the id of entity B. You can simply make persist as follows:
B b = new B();
b.setId(1000); //Assuming that you know the id.
//There is no necessary fill all the object. Just the PK is needed.
A a = new A();
a.setId(100);
a.setAValue("nothing");
a.setB(b);
em.persist(a);
I have tested this with Hibernate as Persistence Provider. If you are using other ORM please specify it.
You should take a look at JPA 2.0.
With JPA you can define a relationship between 2 entites to automatically retrieve the related entities.
I have two hibernate classes: a base class, and an extended class that has additional fields. (These fields are mapped by other tables.)
For example, I have:
#Entity
#Table(name="Book")
public class A {
private String ID;
private String Name;
// ...
}
#Entity
#Table(name="Book")
public class B extends A {
public String node_ID;
// ...
}
public class Node {
public String ID; // maps to B.node_ID
// ...
}
How do I map this in Hibernate? The hibernate documentation states three types of inheritence configurations: one table per class, one table with a type column, and a join table -- none of which apply here.
The reason I need to do this is because class A is from generic framework that's reused over multiple projects, and class B (and Node) are extensions specific to one project -- they won't be used again. In the future, I may have perhaps a class C with a house_ID or some other field.
Edit: If I try the above pseudo-code configuration (two entities mapped to the same table) I get an error that the DTYPE column doesn't exist. The HQL has a "where DTYPE="A" appended.
This is possible by mapping the #DiscriminatorColumn and #DiscriminatorValue to the same values for both classes; this can be from any column you use that has the same data regardless of which type (not sure if it works with null values).
The classes should look like so:
#Entity
#Table(name="Book")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="published")
#DiscriminatorValue(value="true")
public class A {
private String ID;
private String Name;
// ...
}
#Entity
#Table(name="Book")
#DiscriminatorValue(value="true")
public class B extends A {
public String node_ID;
// ...
}
For anyone who got here like me and does not want to have the dtype column but instead want to use the same table for more than one entity as is I would recommend using this
Basically you can create a Base like this
#MappedSuperclass
public abstract class BaseBook<T extends BaseBook> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
... any other variables, getters + setters
}
#Entity
#Table(name= "book")
public class BookA extends BaseBook<BookA>{
//Default class no need to specify any variables or getters/setters
}
#Entity
#Table(name= "book")
public class BookB extends BaseBook<BookB>{
#Column(name = "other_field")
private String otherFieldInTableButNotMapedInBase
... Any other fields, getter/setter
}
From the above we have created base super class which does not have any entity or table mapping. We then create BookA to be default with the Entity + Table mapping. From there we can create other Entities all extending from BaseBook but pointing to one table