JPA entity inheritance: which instance to create for lazy initialization? - jpa

JPA entity inheritance: which instance to create for lazy initialization? For example,
Single table mapping strategy:
Teacher(abstract)
/ \
FullTimeTeacher PartTimeTeacher
Entity School referencing Teacher:
#Entity
public class School {
#ManyToOne(fetch=FetchType.LAZY)
private Teacher manager;
}
When retrieving a School entity from database, the school's manager is lazy, not initialized. Which type of proxy will be instantiated? Teacher is abstract.
The proxy may not match the actual referenced type (Full Time or Part Time Teacher).

I was curious myself, and tested it with the following setup:
#Entity
public class Garage {
#Id
#GeneratedValue
private Long id;
#OneToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
private Car car;
...
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Car {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
...
}
#Entity
public class SportsCar extends Car {
private int hp;
...
#Override
public String toString() {
return "SportsCar [hp=" + hp + ", getId()=" + getId() + "]";
}
}
The test:
Garage garage = new Garage();
SportsCar car = new SportsCar();
car.setHp(350);
garage.setCar(car);
em.persist(garage);
...
Garage garage = em.find(Garage.class, garage.getId());
System.out.println(garage.getCar().getClass());
System.out.println(garage.getCar());
System.out.println(garage.getCar() instanceof SportsCar);
The above prints:
class com.example.Car_$$_jvstd71_f
SportsCar [hp=350, getId()=1]
false
Conclusion: Hibernate will create a proxy of the superclass. That proxy will, however, delegate method calls to the subclass instance.

Related

JPQL query on abstract mother class accessing subclasses properties

I'm facing a problem trying to make a JPA repository on an abstract class.
What I'm willing to do is a method that filters, paginates and returnes 3 differents kind of objects in a single list. Some have shared properties (grouped in AbstractClass).
Here are my different classes :
Mother abstract class
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "te_abstract_data")
#DiscriminatorColumn(name="data_type")
public abstract class AbstractData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "data_type")
#Enumerated(EnumType.STRING)
private DataType dataType;
}
AbstractClass
#MappedSuperclass
public class AbstractClass extends AbstractData {
#ManyToOne
#JoinColumn(name = "fk_obj_id")
private ObjEntity opbj;
}
ClassA & ClassB classes
#Entity
#DiscriminatorValue("CLASS_A")
#Table(name = "te_class_a")
#PrimaryKeyJoinColumn(name="fk_data_id")
public class ClassAEntity extends AbstractClass {
// some fields...
}
ClassC
#Entity
#DiscriminatorValue("CLASS_C")
#Table(name = "te_class_c")
#PrimaryKeyJoinColumn(name="fk_data_id")
public class ClassCEntity extends AbstractData {
// some fields...
}
And here is my repository :
#Repository
public interface DataDao extends JpaRepository<AbstractData, Integer> {
#Query(value =
"SELECT ad " +
"FROM AbstractData ad " +
"WHERE ad.obj.name = :objName " +
// some other filters on various fields
"ORDER BY ad.id ASC"
)
List<AbstractData> findFiltered(
Pageable pageable,
String objName
);
}
The current query in my repository ignores only returns ClassB object, even if ClassA and C's objects are well initialized. I have no idea about what could be wrong...
Any idea how I could solve this ?

Map One to two association

I want to map exactly two players to one team with a bidirectional association. For that i use two attributes at the team Entity (for the player) and one attribute at the player (for the team).
Team class:
#Entity
public class Team implements Serializable {
#Id
private int id;
#OneToOne(mappedBy = "name")
private Player player1;
#OneToOne(mappedBy = "name")
private Player player2;
...
}
Player class:
#Entity
public class Player implements Serializable {
#Id
private String name;
#OneToOne
#JoinColumn(name = "TEAM_ID")
private Team team;
...
}
When deploying i got following error:
Internal Exception: Exception [EclipseLink-7244] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a): org.eclipse.persistence.exceptions.ValidationException
Exception Description: An incompatible mapping has been encountered between [class [...].data.entities.Team] and [class [...].data.entities.Player]. This usually occurs when the cardinality of a mapping does not correspond with the cardinality of its backpointer.
What is the correct way to map a 1:2 association in JPA?
I think there is problem in database releations:
Player and Team has two OneToMany Realation(one player can b in many team)
You Should changed to this:
Team class:
#Entity
public class Team implements Serializable {
#Id
private int id;
#OneToMany
#JoinColumn(name = "player_id_1")
private Player player1;
#OneToMany
#JoinColumn(name = "player_id_2")
private Player player2;
...
}
Player class:
#Entity
public class Player implements Serializable {
#Id
private String name;
#ManyToOne(mappedBy = "player1")
private Set<Team> teamPlayer1;
#ManyToOne(mappedBy = "player2")
private Set<Team> teamPlayer2;
...
}
You have described a OneToMany/ManyToOne relationship in the database, but without any ordering. Try mapping it as such and using the position within the list to define the relationship - you can limit it to allowing only collections of size 2 in code or at the database yourself if you need.
#Entity
public class Team implements Serializable {
#Id
private int id;
#OneToMany(mappedBy = "team")
#OrderColumn
private List<Player> players;
...
}
#Entity
public class Player implements Serializable {
#Id
private String name;
#OneToOne
#JoinColumn(name = "TEAM_ID")
private Team team;
...
}
The addition of the #OrderColumn will create a column in the Player table to track their position within the team list.
This then allows you to always assume player1 will be in the first position, while player2 is second in the list and so can write get/setPlayer1 and get/setPlayer2 methods appropriately - just take care to handle empty lists and null values.

JPA Criteria to query hierarchy of child objects

I have two tables that are represented by following entity object hierarchies:
#Entity
#Table(name = Transport.TABLE_NAME)
#DiscriminatorColumn(name="transport_type", discriminatorType = DiscriminatorType.INTEGER)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Transport {
...
private Date departure;
}
#Entity
#DiscriminatorValue("1")
public class Bicycle extends Transport {
...
#OneToOne(mappedBy = "transport", fetch = FetchType.LAZY)
private BikePassenger passenger;
}
#Entity
#DiscriminatorValue("2")
public class Car extends Transport {
...
#OneToMany(mappedBy = "transport", fetch = FetchType.EAGER)
private List<CarPassanger> passengers;
}
#Entity
#Table(name = Passenger.TABLE_NAME)
#DiscriminatorColumn(name="passenger_type", discriminatorType = DiscriminatorType.INTEGER)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Passenger {
...
private int passengerGUID;
}
#Entity
#DiscriminatorValue("1")
public class BicyclePassenger extends Passenger {
...
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "transportid")
private Bicycle transport;
}
#Entity
#DiscriminatorValue("2")
public class CarPassenger extends Passenger {
...
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "transportid")
private Car transport;
}
Now using JPA Criteria API (or at least JPA) how do I:
Get all transports that have passengers with specific passengerGUID?
Group transports (along with passengers) by departure date?
As I see it #1 should have nice solution but I was able to get out only with 2 subselects for each subtype. Which looks ugly to me.
And finally third question - is it good model at all? From OOP point of view to me it looks ok, but from ORM point of view and easiness of queries it looks not so good...
p.s. I'm using hibernate JPA 2.1

using #Embedabble with a foreign key and manyToMany relation

I wrote an example for the code i am trying to implement, i get an error with Constraint "Student_Teacher_FK" already exists.
the #embiddable class has a foreign key that is created twice with current code.
#Entity
public class Teacher {
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Entity
public class Student{
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Embeddable
public class StudentList implements Serializable {
#ManyToMany
#JoinTable(name = "Student_Teacher",
joinColumns =
#JoinColumn(name = "Student_ID", referencedColumnName = "ID"),
inverseJoinColumns =
#JoinColumn(name = "Teacher_ID", referencedColumnName = "ID")
)
#ForeignKey(name = "Student_Teacher_FK", inverseName = "Teacher_Student_FK")
public List<Student> studentList = new ArrayList<Student>();
}
#Entity
public class HistoryTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class LangTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class RetiredTeacher extends Teacher {
// has no students
}
#embeddable : Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity (http://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html)
As you are declaring it in 2 different entity, jpa will create associated association table (student-teacher) 2 times with associated fk, which is explicitely named, and so created 2 times too with the same name. Here is your error.
I don't think using #embeddable is appropriated for what you're intending to do. A student has is own existence and is not part of teacher itself (not an uml composition / black diamond) so it's not an embeddable entity. Student list should be held by teacher entity using a simple manyToMany association.

Related entities not loaded - EAGER ignored?

Got GlassFish v3. I have an one-to-many entity. The problem is, that EclipseLink seems to ignore the fetch EAGER mode.
Here is my entities.
#Entity
public class Person implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Hobby> hobbies;
// getter and setter
}
A 1:n relationship
#Entity
public class Hobby
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
#JoinColumn
private Person person;
// getter and setter
}
And the bean
#javax.ejb.Remote
public interface Testing
{
public void addTestData();
public List<Person> getTestData();
}
#javax.ejb.Stateless
public class TestingBean implements Testing
{
#javax.persistence.PersistenceContext
private EntityManager entityManager;
public void addTestData()
{
Person p = new Person();
p.setName("JOE");
entityManager.persist(p);
Hobby h1 = new Hobby();
h1.setName("h1");
h1.setPerson(p);
entityManager.persist(h1);
}
public List<Person> getTestData()
{
TypedQuery<Person> gridQuery = entityManager.createQuery("SELECT e FROM Person e", Person.class);
return gridQuery.getResultList();
}
}
EDIT Client:
InitialContext context = new InitialContext();
Testing test = (Testing)context.lookup("java:global/dst2_1/TestingBean");
test.addTestData();
for(Person p: test.getTestData()) {
System.out.println(p.getName());
for(Hobby b : p.getHobbys()) {
System.out.println(b.getName());
}
}
context.close();
Using MySQL - Storing the data works. But if I fetch the data only the person is returned - not hobbies. Coudld you tell me what is wrong in my code?
EDIT sorry have tried so many things ... The code shown as above produces:
Exception Description: An attempt was made to traverse a
relationship using indirection that had a null Session. This often
occurs when a n entity with an uninstantiated LAZY relationship is
serialized and that lazy relationship is traversed after
serialization. To avoid this issue, ins tantiate the LAZY
relationship prior to serialization.
But the Person is returned correctly. Why does it specify LAZY while I am using EAGER?
You code looks correct. I can't see any way that the EAGER could be ignored.
Are you sure you get the error with this attribute, not another one?
Also ensure you recompile and deployed your code correctly. You most like have an old version deployed.
Make the eager object Serializable