JPA #Id on #OneToOne Entity with a Composite Key - java

I have an #Entity class, with an #Id annotation and a #OneToOne annotation on the same field. Usually this would not be a problem, but the entity class of the field with these annotations uses a composite key. This is causing more complications than I anticipated.
Here is the entity class that is posing the problem:
#Entity
public class ReportDetails implements Serializable {
#Id
#OneToOne
private MachineLine machineLine;
}
And here is the MachineLine entity class that is being used as an ID in ReportDetails:
#Entity
#IdClass(MachineLine.MachineLineKey.class)
public class MachineLine {
#Id
#ManyToOne
private Machine machine;
#Id
private long lineIndex;
public static class MachineLineKey implements Serializable {
private Machine machine;
private long lineIndex;
}
}
I have left out any extra fields and the getters and setters from these class definitions, to save space.
When I try to run my application it gives the following exception:
java.lang.IllegalArgumentException: This class [class ReportDetails] does not define an IdClass
When I put an #IdClass annotation on ReportDetails it then requires defining the individual fields of whatever class I define in #IdClass, like in MachineLine. However, I am trying to avoid doing this, in favour of having the whole MachineLine entity returned whenever a ReportDetails entity is retrieved from the database.
Is there a way of having MachineLine as the ID field of ReportDetails, without having to define extra fields within ReportDetails?

This is what JPA calls a "derived identity". You might try something like this:
ReportDetails:
#Entity
public class ReportDetails implements Serializable {
// all attributes map by the relationship: AttributeOverride is not allowed
#EmbeddedId
private MachineLine.Id id;
#MapsId
#JoinColumns({
#JoinColumn(name="machineId", referencedColumnName="machineId"),
#JoinColumn(name="machineLineIndex", referencedColumnName="index")
})
#OneToOne
private MachineLine machineLine;
// ...
}
MachineLine:
#Entity
public class MachineLine {
#EmbeddedId
private Id id;
#MapsId("machineId") // maps machineId attribute of embedded id
#ManyToOne
private Machine machine;
// ...
#Embeddable
public static class Id implements Serializable {
private long machineId; // corresponds to PK type of Machine
private long index;
// ...
}
}
Machine:
#Entity
public class Machine {
#Id
private long id;
#OneToMany(mappedBy = "machine")
private List<MachineLine> lines;
// ...
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.

Related

Hibernate: How to map a class that "has-a" subclass to a single table?

I have a class BacktestResult that contains a property BacktestInputs. I want hibernate to create a single table that contains the contents of both. Here's what I have so far:
#Entity
#Table(name="backtest_result")
public class BacktestResult {
#Id
Long backtest_id;
public int userId;
BacktestInputs inputs;
#OneToMany
#OrderColumn(name="backtest_id")
public BacktestChartDataPoint[] series;
//getters and setters
The BacktestChartDataPoint class looks like this:
#Entity
#Table(name="backtestchart_datapoint")
public class BacktestChartDataPoint {
#Id
public Long id;
public Long backtest_id;
#ManyToOne
#JoinColumn(name="backtest_id", nullable=false, insertable=false, updatable=false)
private BacktestResult backtestResult;
The BacktestInputs class looks like this:
public class BacktestInputs {
public String strategy_name;
public String version;
etc...
As long as I comment out the BacktestInputs in BacktestResult, hibernate correctly creates 3 tables:
backtest_result
backtest_result_series
backtestchart_datapoint
But I want the contents of BacktestInputs to be included in the BacktestResult table. If I uncomment it, get error:
Could not determine type for:
net.tekknow.moneymachine.model.BacktestInputs, at table:
backtest_result, for columns: [org.hibernate.mapping.Column(inputs)]
What is the proper way to handle class hierarchy with "has-a" relationships, NOT "is-a" relationships?
You need to annotate the class BacktestInputs with #Embeddable
(see https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#embeddables)

Spring and JPA 2.0 - Composite key in many to many relationship with extra column

I have two entities Foo and Bar in many-to-many relationship. The joining entity is FooBar, and since this entity has another property (its own id), I used #ManyToOne on the owner side (FooBar) and #OneToMany in dependent entities (Foo and Bar). How to create a FooBarRepository extending CrudRepository without the explicit composite key field inside FooBar? Ideally, I don't want to change the members of my FooBar class.
I tried to use #IdClass, but I don't want to have fields fooId and barId inside FooBar and I am getting this exception:
Caused by: org.hibernate.AnnotationException: Property of #IdClass not found in entity com.nano.testers.test.FooBar: barId
I also tried to follow the documentation of IdClass and reference columns by name explicitly, but I failed (maybe the solution lies somewhere here?)
The names of the fields or properties in the primary key class and the primary key fields or properties of the entity must correspond and their types must be the same.
I tried to change the names of fields inside Foo and Bar to just id, so that they would be referenced as foo_id and bar_id in the joining table, but the exception was the same.
I don't want to use #EmbeddedId, if that means I need to have a field of FooBarPk type inside the FooBar class.
#Entity
public class Foo {
#Id
private Long fooId;
#OneToMany(mappedBy = "foo", cascade = CascadeType.ALL)
private Set<FooBar> foobars;
}
#Entity
public class Bar {
#Id
private Long barId;
#OneToMany(mappedBy = "bar", cascade = CascadeType.ALL)
private Set<FooBar> foobars;
}
#Entity
//#IdClass(FooBarPk.class)
public class FooBar implements Serializable {
#Id
private Long fooBarId;
#Id
#ManyToOne
#JoinColumn
private Foo foo;
#Id
#ManyToOne
#JoinColumn
private Bar bar;
}
public class FooBarPk implements Serializable {
private Long fooId;
private Long barId;
private Long fooBarId;
}
public interface FooBarRepository extends CrudRepository<FooBar, FooBarPk> {
}
It looks like the names of the fields in the composite key class have to be the same as names of the referenced entities. I think these names don't follow the clean code principles, but I will have to live with this solution for now.
public class FooBarPk implements Serializable {
private Long foo;
private Long bar;
private Long fooBarId;
}

How to query jpa entity in JOINED inheritance, without creating table for same?

I am trying to implemet inheritance hierarchy as mentioned in below image using hibernate Joined strategy.
Since Joined strategy creates table for entity regardless of entity class is Concrete or Abstract.
I don't want to create separate table for "CompanyEmployee" so I declared it as mapped superclass, but I should able to query this class/subclasses in polymorphic way.
Since it is mapped superclass I can't do this, and if I declare it to be entity it will create table which I want to avoid.
So, is there any way I can achieve this? I am thinking about mixed inheritance but from below quote it doesn't seems to be a good solution.
Mixed Inheritance
We should begin this section by saying that the practice of mixing inheritance types within a single
inheritance hierarchy is currently outside the specification. We are including it because it is both useful
and interesting, but we are offering a warning that it might not be portable to rely on such behavior,
even if your vendor supports it.
Inheritance hierarchy
#Entity
#Table(name="j_employee")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="emp_type", discriminatorType=DiscriminatorType.STRING)
public abstract class JEmployee extends AuditLog implements Serializable {
#Id
#Basic
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="employee_id")
private Integer employeeId;
#Basic
#Temporal(TemporalType.DATE)
#Column(name="join_date")
private Date joinDate;
#OneToOne
#JoinColumn(name="person_id", nullable=false)
private Person person;
#Column(name="emp_type", updatable=false, insertable=false)
private String empType;
//Getters and Setters
}
#Entity
#Table(name="j_contract_employee")
#DiscriminatorValue(value="JContractEmployee")
#PrimaryKeyJoinColumn(name="contract_employee_id", referencedColumnName="employee_id")
public class JContractEmployee extends JEmployee implements Serializable {
#Basic
#Column(name="daily_rate")
private Integer dailyRate;
#Basic
#Column(name="term")
private Integer term;
//Getters and Setters
}
//Don't want to create table for this class, but I should able to query this clas/subclasses in polymorphic way
#MappedSuperclass
public abstract class JCompanyEmployee extends JEmployee implements Serializable {
#Basic
#Column(name="vacation")
private Integer vacation;
//Getters and Setters
public Integer getVacation() {
return vacation;
}
public void setVacation(Integer vacation) {
this.vacation = vacation;
}
}
#Entity
#Table(name="j_part_time_employee")
#Access(AccessType.FIELD)
#DiscriminatorValue(value="JPartTimeEmployee")
#PrimaryKeyJoinColumn(name="part_time_employee_id", referencedColumnName="employee_id")
public class JPartTimeEmployee extends JCompanyEmployee implements Serializable {
#Basic
#Column(name="hourly_rate")
private Integer hourlyRate;
//Getters and Setters
}
#Entity
#Table(name="j_full_time_employee")
#Access(AccessType.FIELD)
#DiscriminatorValue(value="JFullTimeEmployee")
public class JFullTimeEmployee extends JCompanyEmployee implements Serializable {
#Basic
#Column(name="salary")
private Integer salary;
#Basic
#Column(name="penion")
private Integer pension;
//Getters and Setter
}

Hibernate, OneToMany, AnnotationException

I have a problem with OneToMany annotation
#Entity
#Table(name = "RESULT_HISTORY")
public class ResultHistoryImpl implements ResultHistory, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany
private final Set<Game> games = new HashSet<>();
...
}
and class
#Entity
#Table(name = "GAME")
public class GameImpl implements Game, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
...
}
I have more fields, setters/getters and constructors
I use spring and in my configuration files I have packagesToScan where I put packages of those classes
The problem is that I get org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class
I read many topics on stackoverflow, so the problem isn't using org.hibernate.annotation etc
What you tried is not possible. Game is an interface and you cannot map an interface to DB, because hibernate does not know which implementation to use. But what you can do, is
use in all your entity classes the GameImpl class directly. I mean correct your ResultHistoryImpl class to this: #OneToMany private final Set<GameImpl> games = new HashSet<GameImpl>();
If you want to have another abstraction, add an abstract class AbstractGame implements Game and annotate it with #Entity and use it instead of the Game interface.
Use #MappedSuperclass annotation on Game.
For #OneToMany you have to provide a mappedBy property which points to a field name in the other class. In your case:
public class ResultHistoryImpl {
...
#OneToMany(mappedBy = "resultHistory")
private final Set<GameImpl> games = new HashSet<>();
}
public class GameImpl {
#ManyToOne
#JoinColumn(name = "result_history")
private ResultHistoryImpl resultHistory;
}

Shared PK in entity mismatch

I'm developing an example web-application, using JPA 2.0 entities, Hibernate 3.6.2 and Spring 3. The example contains two tables in a one-to-one relationship, the parent entity is Client and the child is Address, the PK in Address references the Client table (identifying relationship).
Running the JUnit tests, I've noted a peculiar problem with these two entities, the problem it's that the child entity persists with the (parentId + 1), my mappings are as follows:
#Entity
public class Client implements Serializable{
private Long clientId;
private Address address;
//Other fields
#Id
#GeneratedValue
public Long getClientId(){return this.clientId;}
public void setClientId(Long id){this.clientId=id;}
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="clientid",referencedColumnName="fk_clientid")
public Address getAddress(){return this.address;}
}
And the child entity:
#Entity
public class Address implements Serializable{
private Long fkClientId;
private Client client;
//Other fields
#Id
#GeneratedValue
public Long getFkClientId(){return this.fkClientId;}
public void setFkClientId(Long id){this.fkClientId=id;}
#OneToOne(mappedBy="address")
public Client getClient(){return this.client;}
}
In my test methods I link both objects using their setters, but after persist the entities and execute the line:
assertEquals(client.getClientId, client.getAddress().getFkClientId);
The test fails with the exception
java.lang.AssertionError: expected:<654> but was:<655>
I've readed similar questions and problems, but almost all of them are from JPA 1.0, it's supossed that in JPA2.0 the shared keys are automatically assigned. What am I missing?
The correct version of such a mapping is shown in javadoc of #OneToOne. Note that the side with derived identity should be the owning side of relationship (without mappedBy):
#Entity
public class Client implements Serializable {
#Id
#GeneratedValue
private Long clientId;
#OneToOne(mappedBy = "client", cascade = CascadeType.ALL)
private Address address;
...
}
#Entity
public class Address implements Serializable {
#Id
private Long fkClientId;
#OneToOne
#MapsId
#JoinColumn(name = "fk_clientid")
private Client client;
...
}

Categories