I have two entity-classes A and B. For A there is only one instance of class B. Through a lifecicle of application I need to create new instnace of B for A. But for history log I need to store previous instance of B with links to an this A instance. So I created next mapping:
#Entity
class A {
#OneToOne
#JoinColumn(name="B_ID")
B b;
}
#Entity
class B {
#OneToOne
#JoinColumn(name="A_ID")
A a;
}
///////
CREATE TABLE A_TABLE (ID uuid, B_ID uuid, FOREIGN KEY (B_ID) REFERENCES B_TABLE(ID));
CREATE TABLE B_TABLE (ID uuid, A_ID uuid, FOREIGN KEY (A_ID) REFERENCES A_TABLE(ID));
I already have some correct data in my tables, but each time, when I trying to get any instance of A class, the b field is null. So, what am I doing wrong?
UPD_0
Ok, here what I'm realy dealing with:
#MappedSuperclass
public abstract class BaseUuidEntity {
#Id
#Column(name = "ID")
#Persistent
protected UUID id;
}
#MappedSuperclass
public class StandartEntity extends BaseUuidEntity { ... }
#MappedSuperclass
public abstract class AbstractEntity extends BaseUuidEntity { ... }
#Entity(name = "CardEntity")
#Table(name = "TBL_CARD")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "CARD_TYPE", discriminatorType = DiscriminatorType.INTEGER)
#DiscriminatorValue("0")
public class CardEntity extends AbstractEntity { ... }
#Entity("aEntity")
#DiscriminatorValue("1")
#Table(name = "A_TABLE")
class A extends CardEntity {
#OneToOne
#JoinColumn(name="B_ID")
B b;
public String getBName() {
return b.getName(); // NullPointerException
}
}
#Entity("bEntity")
#Table(name = "B_TABLE")
class B extends StandartEntity {
#Column(name="name")
String name;
public String getName() {
return this.name;
}
}
-- A_TABLE (ID, B_ID)
'41a2647d-2ef6-f507-92ee-bff0f784b6f3', '5d3c66da-b377-11e4-bc9c-1008b124eacf'
-- B_TABLE (ID, A_TABLE, NAME)
'5d3c66da-b377-11e4-bc9c-1008b124eacf', '41a2647d-2ef6-f507-92ee-bff0f784b6f3', 'Some text'
I'm getting all A entities (in my case - it's one)
select e from aEntity e
and trying to get name of b entity in each of them, but getting nullpointer, cause b is null;
I'm not sure if it will make any difference, but try changing the code in getBName() to use the getter instead of directly referencing the instance variable b:
public String getBName() {
return getB().getName();
}
instead of:
public String getBName() {
return b.getName(); // NullPointerException
}
I suspect that this could be the issue because JPA implementations create proxies for entities, which are usually subclasses of your entities, and these proxies override your getters and setters. So if you don't use the getter it could be that the instance variable does not get initialized properly.
you need mappedBy anotation: http://en.wikibooks.org/wiki/Java_Persistence/OneToOne
to inform java who is in charge of this relationship and when the 'foreign'id' is storred. You also need the foreign key in only one of the entities not both!
Related
I have read the O/R Mapping of Hibernate and I just can't seem to get past the part on polymorphism.
According to https://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html/ch05.html,
Implicit polymorphisms means that instances of the class will be
returned by a query that names any superclass or implemented interface
or class, and that instances of any subclass of the class will be
returned by a query that names the class itself
whereas
Explicit polymorphisms means that class instances will be returned
only by queries that explicitly name that class. Queries that name the
class will return only instances of subclasses mapped
I just want to understand how these 2 work. Can somebody explain these terms using an example(doesn't have to be too complex) with the use of code? I would appreciate your help
First of all the org.hibernate.annotations.Entity annotation is deprecated now. You should use the #Polymorphism annotation instead.
Now, imagine that you have the following schema:
create table TST_STUDENT
(
st_id int not null,
st_name varchar(200),
primary key (st_id)
);
insert into TST_STUDENT values (1, 'Kostya'), (2, 'Yulia'), (3, 'Borya'), (4, 'Misha');
create table TST_TEACHER
(
tcr_id int not null,
tcr_first_name varchar(200),
tcr_last_name varchar(200),
primary key (tcr_id)
);
insert into TST_TEACHER values (1, 'Mikhail', 'Bulgakov'), (2, 'Leo', 'Tolstoy');
and the following mapping:
public interface Person
{
Long getId();
String getName();
}
#Entity
#Table(name = "TST_STUDENT")
public class Student implements Person
{
#Id
#Column(name = "st_id")
private Long id;
#Column(name = "st_name")
private String name;
public Student()
{
}
// getters / setters
}
and Teacher entity:
import org.hibernate.annotations.Polymorphism;
import org.hibernate.annotations.PolymorphismType;
#Entity
#Table(name = "TST_TEACHER")
// #Polymorphism(type = PolymorphismType.EXPLICIT)
public class Teacher implements Person
{
#Id
#Column(name = "tcr_id")
private Long id;
#Column(name = "tcr_first_name")
private String name;
#Column(name = "tcr_last_name")
private String lastName;
public Teacher()
{
}
// getters / setters
}
Now, if you run the following query:
List<Person> persons = em.createQuery("select p from com.your.entities.Person p", Person.class).getResultList();
you will get all rows from the TST_STUDENT table plus all rows from the TST_TEACHER table.
But, if you uncomment this line:
#Entity
#Table(name = "TST_TEACHER")
#Polymorphism(type = PolymorphismType.EXPLICIT) // now we use explicit polymorphism for the Teacher entity
public class Teacher implements Person
The mentioned above query will return only rows from the TST_STUDENT table.
This is what this annotation mean.
By default, when you query a base class entity, the polymorphic query will fetch all subclasses belonging to the base type. You can even query interfaces or base classes that don’t belong to the JPA entity inheritance model.
P.S. See also this part of documentation.
I have two tables A and B:
#Entity
public class A {
}
#Entity
public class B {
private final A a;
private String someBSpecificField;
}
Entity A is already Coded nicely and mapped to an existing table. My job is to create B and for some reason I prefer composition over inheritance between A and B. At the same time I want to have single table for A and B to avoid joins when reading. Can I do like this:
#Entity
#Table(name = "a")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "a_type_enum", discriminatorType =
DiscriminatorType.INTEGER)
#DiscriminatorValue("100")
public class A {
}
#Entity
#Table(name = "a")
#DiscriminatorValue("500")
public class B {
private final A a;
private String someBSpecificField;
}
If the type is a complex object then you need to annotate that type with #Embeddable and use it in your #Entity class.
if you want to override the attribute names for your #Embeddable class so they have different column names, you can do so with the #AttributeOverrides annotation.
Let's say I have a superclass called Vehicle like this:
#Entity
public class Vehicle {
private LocalDateTime dateTimeOfCreation;
}
And, let's say I have a subclass called Car like this:
#Entity
public class Car extends Vehicle{
}
And, I have a I want to select all cars that are created after some date. And, although dateTimeOfCreation field is in superclass, I need to be able to query the subclass for the reason that is not easy for me to explian.
I tried this query: SELECT c from Car c WHERE c.dateTimeOfCreation > :dateTime
However, I get an ORA-00904: Invalid Identifier error, which is in some way logical because the class car doesn't have that field and Cars. Also, I am using a JOINED INHERITANCE, so every class has it's table and dateTimeOfCreation is stored in Vehicle table. That's something I can't change, again for the reason hard to explain.
EDIT: The code I gave is just an example. In reality. The Vehicle is a part of a framework that I shouldn't change, so it already has #Entity annotation.
Car is a class I implemented, that also needs to be an entity. The framework, however, keeps track of times of creation and I need to use that information in a query. However, I can't query Vehicle directly because, I need just the cars, not trucks, bikes etc.
You declare Vehicle as en entity as you have annotated it with javax.persistence.Entity You should not or otherwise you should declare it as an abstract class.
You can also specifying mapped superclasses by annotating the Vehicle class with the javax.persistence.MappedSuperclass annotation.
For example :
#MappedSuperclass
public class Vehicle {
private LocalDateTime dateTimeOfCreation;
}
The solution is suitable if you don't want to query Vehicle entities.
class Vehicle
#Entity(name = "vehicle")
#Inheritance(strategy= InheritanceType.JOINED)
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private LocalDateTime dateTimeOfCreation;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public Date getDateTimeOfCreation() {
return date;
}
public void setDateTimeOfCreation(Date date) {
this.date = date;
}
}
Class Car:
#Entity(name = "car")
#PrimaryKeyJoinColumn(name = "vehicle_id")
public class Car extends Vehicle {
}
Dao class:
public class Abc {
-----your logic-----
Session session = HibernateUtil.getSession();
Query query = session.createQuery("from car as c where c.dateTimeOfCreation >= :date");
query.setParameter("date", LocalDateTime.now());
List<RegularEmployee> regularEmployees = query.list();
}
I have 2 Object A and B, which have same attributes, but different table.
#Entity
#Table(name = "A")
public class A {
#Id
private Integer id;
...
}
and
#Entity
#Table(name = "B")
public class B {
#Id
private Integer id;
...
}
it will cause duplicate code because every attributes are same.
What is best practice to do this kind of problem?
I'd create a #MappedSupperclass for this, and extend from it. It would look like this
#MappedSuperclass
public class Common {
#Id
private Integer id;
}
#Entity
#Table(name = "A")
public class A extends Common {
...
}
#Entity
#Table(name = "B")
public class B extends Common {
...
}
This doesn't create an entity hierarchy, but only moves common entity attributes in a super class. Often used for id, version, createdBy etc.
I have two tables in database, A and B. Table B has an id composed of two fields. One of them is a foreign key to A. Id of A is automatically generated on insert by a sequence.
A:
ID (PK)
(*other fields*)
B:
SOME_FIELD (PK)
A_ID (PK, FK to A)
I have mapped the two tables in JPA (Hibernate) following JPA specification, in this way:
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#SequenceGenerator(name = "A_SEQ", sequenceName = "A_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "A_SEQ")
#Column(name = "ID")
private Long id;
(...)
}
#Entity
#Table(name = "B")
public class B implements Serializable {
#EmbeddedId
#AttributeOverride(name = "someField", column = #Column(name = SOME_FIELD))
private BPK pk;
#MapsId("aId")
#ManyToOne
#JoinColumn(name = "A_ID")
private A a;
(...)
}
#Embeddable
public class BPK implements Serializable {
private Long aId;
private String someField;
#Override
public boolean equals(Object o) {
(...)
}
#Override
public boolean hashCode() {
(...)
}
(...)
}
The problem is that when I try to save an B object calling entityManager.persist(b), where b.a is set to an A object, which exists already in database, I get an exception:
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: package.name.A
I don't know why this happens. I'm trying to save object of class B, not A. Is my entity class wrong? Or maybe I shouldn't use persist here?
It could be that the entity A is no longer being held by entity manager. Have you tried setting B.a with a "fresh" instance of A?
b.setA(get(b.a));
entityManager.persist(b);
The get(b.a) method can be whatever you usually use to find entity A from your datasource e.g. entityManager.getReference(A.class, a.id);