JPA Criteria to query hierarchy of child objects - jpa

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

Related

Having abstract data type for attribute in JPA

I have following entities -:
#MappedSuperclass
abstract class Enterprise {
}
Two classes implementing Enterprise
#Entity
class Vendor() : Enterprise {
#OneToMany
#JoinColumn(name="contactPerson_id")
lateinit var contactPerson: List<ContactPerson>
}
#Entity
class Customer() : Enterprise {
#OneToMany
#JoinColumn(name="contactPerson_id")
lateinit var contactPerson: List<ContactPerson>
}
And for ContactPerson Class we have reference for each type of Enterprise
#Entity
#Table(name = "CONTACT")
class ContactPerson {
#JoinColumn(name = "vendor_id")
#ManyToOne
var vendor: Vendor
#JoinColumn(name = "vendor_id")
#ManyToOne
var customer: Customer
#Id
val id: Int? = null
}
But instead of keeping two reference i want to have one reference of abstract type Enterprise something like this
#Entity
#Table(name = "CONTACT")
class ContactPerson {
#JoinColumn(name = "vendor_id")
#ManyToOne
var enterprise: Enterprise
#Id
val id: Int? = null
}
But this give me error "Many To One' attribute type should not be 'Mapped Superclass"
which i understand is due to the fact that Enterprise is not an Entity
and will not be mapped to a table which is exactly what i want .
How can i achieve this without having an Entity superclass

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 entity inheritance: which instance to create for lazy initialization?

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.

Spring data elasticsearch - Eagerly load #Entity nested set

Given this entity:
#Entity
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "foo")
public class Foo implements Serializable {
#Column(name = "created_date", nullable = false)
private Instant createdDate;
#OneToMany(mappedBy = "foo", cascade = {CascadeType.ALL})
#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Bar> bars = new HashSet<>();
}
and this repository interface:
public interface FooSearchRepository extends ElasticsearchRepository<Foo, Long> {}
How can I get the bars to be eagerly loaded when calling Iterable<T> search(QueryBuilder var1);. I thought this might work:
public interface FooSearchRepository extends ElasticsearchRepository<Foo, Long> {
#Override
#EntityGraph(attributePaths = {"bars"})
Iterable<Foo> search(QueryBuilder queryBuilder);
}
... as per here, but it doesn't appear to work as expected i.e. bars not retrieved, but createdDate is.
Any suggestions appreciated.
Thanks