JPQL: Difference between EclipseLink and Hibernate - java

I already asked about my situation and didn't find a proper solution. After some additional search I think I know the source problem although don't know how to resolve it. As mentioned I have:
#Table(name = "role__parent")
#IdClass(RoleAssociationKey.class)
#Data
public class RoleAssociation implements Serializable {
#Id
#JoinColumn(name = "has_parent_role_id")
private Role node;
#Id
#JoinColumn(name = "is_parent_for_role_id")
private Role parent;
...
}
#Data
class RoleAssociationKey implements Serializable {
private static final long serialVersionUID = 1L;
private int node;
private int parent;
}
and I have
#Table(name = "role")
#Data
public class Role implements IRole {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(mappedBy = "node", orphanRemoval = true, cascade = { CascadeType.ALL })
private List<RoleAssociation> parentRoles;
...
Up to this point I think nothing special. I have the query:
#NamedQuery(name = "Role.findParents", query =
"SELECT r FROM Role r JOIN RoleAssociation ra ON r.id = ra.parent.id WHERE ra.node.id = :id")
with the purpose to all set parents. When I compile it Hibernate complainsleft and right hand sides of a binary logic operator were incompatible [integer : component[node,parent]]
Since the statement works in EclipseLink I have no clue how to change it into a working Hibernate one. Help would be highly appreciated.

After some struggels I finally figured the root cause of this problem. I'm aware that the SQL might be improved nevertheless I fail at other similar spots.
Hibernate requires to have a matching pair for #OneToMany relation.
In my query I refer to the parent of my role. The solution is
#Data
public class RoleAssociation implements Serializable {
#Id
#JoinColumn(name = "has_parent_role_id")
#ManyToOne // <<<<==== rerequired for Hibernate
private Role node;
#Id
#JoinColumn(name = "is_parent_for_role_id")
#ManyToOne // <<<<==== rerequired for Hibernate
private Role parent;
I have no clue why Hibernate complains while EclipseLink can fetch the required information. Nevertheless with this additional annoation the code works!

Related

#OneToMany - how to skip flagged child elements?

2 entities that I have: Country and User
#Entity(name = "Country")
#Table(name = "countries")
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL,
orphanRemoval = true)
private List<User> users = new ArrayList<>();
}
#Entity(name = "User")
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private boolean removed;
}
Entity User has an attribute, removedtype boolean and we need to skip/ignore all User's that have removed equals true.
Does Hibernate provide any suitable mechanism to achieving my goal?
P.S: Google says that potentially I can use annotations like: #JoinColumn, #JoinColumnOrFormula, but based on docs that I have read, annotations mentioned above belong to #ManyToOne scenario, in my particular case I have #OneToMany relation.
With hibernate, using #Where on the entity should do the trick. This will prevent any user to be loaded which has the removed flag set to true. You might find more information in the hibernate docs
If you only want this to be on the collection, you can add the annotation on the relationship
#Entity(name = "Country")
#Table(name = "countries")
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL,
orphanRemoval = true)
#Where( clause = "removed = false" ) // <- here
private List<User> users = new ArrayList<>();
}
Alternatively, if you don't want to load any removed user at all, the annotation can be added to the class.
#Entity(name = "User")
#Table(name = "users")
#Where( clause = "removed = false" ) // <- here
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private boolean removed;
}
A word of caution, I haven't used this, but I assume that if you have other entities with linked to a user which has the flag set to true, you'll need to update the annotations to allow nulls.
For example, imagine an ecomerce site, an Order is linked to a user. Loading an Order linked to a removed user will fail, or you can ammend the annotations so it returns an order with a null user. I hope this makes sense!

JPA one to one mapping creates multiple query when child entity is not found

I have a parent entity 'contracts' that has a one-to-one relation with another entity 'child-contract'. the interesting thing is that the mapping field ('contract_number')id not a primary key-foreign key but is rather a unique field in both the tables. Also it is possible for a contracts to not have any child contract altogether. With this configuration I have observed hibernate to generate 1 additional query every time a contracts does not have a child-contract. I filed this behavior very strange. Is there a way to stop these unnecessary query generation or have I got something wrong.
below is a piece of my code configuration.
#Data
#Entity
#Table(name = "contracts")
public class Contracts implements Serializable {
#Id
#JsonIgnore
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
#OneToOne(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JsonProperty("crm_contracts")
#JoinColumn(name = "contract_number", referencedColumnName = "contract_number")
private ChildContract childContract ;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "child_contract")
#BatchSize(size=1000)
public class ChildContract implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#JsonProperty("id")
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
}
Please help.
Thank-you
You can use NamedEntityGraph to solve multiple query problem.
#NamedEntityGraph(name = "graph.Contracts.CRMContracts", attributeNodes = {
#NamedAttributeNode(value = "crmContract") })
Use this on your repository method as
#EntityGraph(value = "graph.Contracts.CRMContracts", type = EntityGraphType.FETCH)
// Your repo method in repository

Hibernate JOIN of Unrelated Entities Out of Scope in the Generated SQL

Since Hibernate 5.1, it started to offer the feature for us to join two unrelated entities as we do in native SQL. It is a fantastic feature! However, I recently encountered an unexpected behavior of this feature. I had a query with mixed JOINs (Left Joins and Inner Joins) with both related entities and unrelated entities. In the generated SQL, all of the unrelated entities JOINs are place in the bottom of the query, which caused this exception:
com.microsoft.sqlserver.jdbc.SQLServerException: The multi-part identifier "tlterm6_.term_id" could not be bound.
I'm baffled about how that happened and why was the feature implemented in that way (They must have a good explanation, but I have not found any solutions or explanations online yet).
Does anyone have an idea of a workaround or how to fix that?
The application is running on Hibernate 5.4.6 and SQL Server database.
Sample Entity Definition:
#Entity
#Table(name = "student")
Public class Student implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
private Integer id;
#Column
private String first_name;
#Column
private String first_name;
#OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true)
private List<College> colleges = new ArrayList<>();
// ...Other details and getters/setters omitted
}
#Entity
#Table(name = "college")
Public class College implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
private Integer id;
#Column
private String name;
#Column
private String description;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "student_id")
private Student student;
// ...Other details and getters/setters omitted
}
Example:
FROM Student student
JOIN Class clazz ON student.id = clazz.student_id
JOIN student.colleges college
Generated SQL:
FROM dbo.student AS student
INNER JOIN dbo.college AS college ON student.id = college.student_id
INNER JOIN dbo.class AS clazz ON student.id = clazz.student_id
The expected generated SQL should be following the same order of the JOINs, however, it places the Related/Mapped entities Joins to the top and moves the Unrelated/Unmapped entities Joins to the bottom.

Spring Jpa Data Repository save (update) with LinkedEntity for ManyToMany relationship

There are 2 entities (lets say Rule and Label) with many-to-many relationship using linked entity
as per hibernate reference documentation
Rule enity:
#Entity
#Table(name = "rule")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "name")
public class Rule implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NaturalId
#NotBlank
#Column(unique = true)
private String name;
#Lob
#Column(columnDefinition = "TEXT")
private String content;
#OneToMany(mappedBy = "rule", cascade = {CascadeType.PERSIST,
CascadeType.MERGE})
private List<RuleLabel> labels = new ArrayList<>();
...
Label entity:
#Entity
#Table(name = "label")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Label implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
private String name;
#OneToMany(mappedBy = "label", cascade = {CascadeType.PERSIST,
CascadeType.MERGE})
private List<RuleLabel> rules = new ArrayList<>();
...
Link entity:
#Entity
public class RuleLabel implements Serializable {
#Id
#ManyToOne
private Rule rule;
#Id
#ManyToOne
private Label label;
...
Repositories:
#Repository
public interface LabelRepository extends JpaRepository<Label, Long>
...
#Repository
public interface RuleRepository extends JpaRepository<Rule, Long>
...
Creating new entity via RuleRepository.save(Rule) works fine, but when I'm trying to update existing entity (the same method RuleRepository.save(Rule), but entity to be saved contains id field) it leads to infinite loop of Hibernate: select... queries:
Hibernate: select rule0_.id as id1_7_1_, rule0_.is_active as is_activ2_7_1_, rule0_.content as content3_7_1_, rule0_.is_deleted as is_delet4_7_1_, rule0_.import_section as import_s5_7_1_, rule0_.name as name6_7_1_, rule0_.rule_configuration as rule_con7_7_1_, labels1_.rule_id as rule_id1_8_3_, labels1_.label_id as label_id2_8_3_, labels1_.rule_id as rule_id1_8_0_, labels1_.label_id as label_id2_8_0_ from rule rule0_ left outer join rule_label labels1_ on rule0_.id=labels1_.rule_id where rule0_.id=?
and StackOverflowError as a result
java.lang.StackOverflowError: null
at com.mysql.jdbc.ServerPreparedStatement.getInstance(ServerPreparedStatement.java:332)
...
(LabelRepository acts in the same manner)
How it can be fixed?
Update:
After changing fetch strategy to Lazy
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Rule rule;
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Label label;
infinite loop problem has gone, but new one has appeared - related entities are not being populated and when Hibernate is trying to insert values into link table
Hibernate: insert into rule_label (rule_id, label_id) values (?, ?)
we get
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'rule_id' cannot be null
Okay, well I've always used an EmbeddableId for link entities with JPA. I haven't tried the hibernate example you refer to in terms of using cascade to do the work for me. It could be interesting but there are some differences between pure JPA and Spring Data Repositories. By using an EmbeddableId you can create a separate spring repository for the link entity. Then you manage the relationships yourself. If you don't want to do that then you should use a ManyToMany annotation, but the link entity allows you to create link entity attributes, not shown here. This code will work for you and get you to point B and you can experiment from there:
#Entity
public class Label {
#Id #GeneratedValue private Long id;
#OneToMany(mappedBy = "ruleLabelId.labelId")
private List<RuleLabel> rules = new ArrayList<>();
#Entity
public class Rule {
#Id #GeneratedValue private Long id;
#OneToMany(mappedBy = "ruleLabelId.ruleId")
private List<RuleLabel> labels = new ArrayList<>();
#Entity
public class RuleLabel {
#EmbeddedId
private RuleLabelId ruleLabelId;
#SuppressWarnings("serial")
#Embeddable
public class RuleLabelId implements Serializable {
private Long ruleId;
private Long labelId;
public interface RuleRepository extends JpaRepository<Rule, Long> {
#Query("from Rule r left join fetch r.labels where r.id = :id")
public Rule getWithLabels(#Param("id") Long id);
}
public interface RuleLabelRepository extends JpaRepository<RuleLabel, RuleLabelId> {}
and to use it:
Rule rule = new Rule();
Label label = new Label();
ruleRepo.save(rule);
labelRepo.save(label);
RuleLabel ruleLabel = new RuleLabel();
RuleLabelId ruleLabelId = new RuleLabelId();
ruleLabelId.setRuleId(rule.getId());
ruleLabelId.setLabelId(label.getId());
ruleLabel.setRuleLabelId(ruleLabelId);
ruleLabelRepo.save(ruleLabel);
rule = ruleRepo.getWithLabels(1L);
System.out.println(rule + Arrays.toString(rule.getLabels().toArray()));
Yes because its what you are telling hibernate to do.
By default, all #ManyToOne and #OneToOne associations are EAGER loaded, so when it querying Rule then its also querying RuleLabel and then inside there is Rule again which is causing infinite select queries. It's better to have them LAZY loaded.
You can do field lazy load like this #ManyToOne(fetch=FetchType.LAZY)
This is what JPA 2.0 spec say about defaults:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
A good read on Lazy and Eager loading

Hibernate not setting foreign key

I'm trying to learn Hibernate with this simple example but I'm having so trouble with the foreign key which remains "null" in the database.
#Entity
#Table(name = "tb1")
public class Track {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id_tb1", unique= true)
private int id_tb1;
#Column(name = "title")
private String title;
#ManyToOne
#JoinColumn(name="id_tb2")
private tb2 cd;
And this is the second class
#Entity
#Table(name = "tb2")
public class CD {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id_tb2", unique = true)
private int id_tb2;
#Column(name="title")
private String title;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL,mappedBy = "cd")
private List<tb1> tracks = new ArrayList<tb1>();
I save like this:
SessionFactory factory = new Configuration().configure("/resources/hibernate.cfg.xml").buildSessionFactory();
Session session1 = factory.openSession();
session1.beginTransaction();
session1.save(tb2);
session1.getTransaction().commit();
but when Isavethe id_tb2 (in the table tb1) is not set and it remains null. What I'm missing?
The problem you have to set the relation on both sides for a bidirectional relationship.
So you have to set your relationship forCD and your Track object and persist/merge them afterwards.
Without seeing to much of your code you have to do something like.
cd.getTracks().add(track);
track.setCD(cd);
session1.save(track);
session1.save(cd);
See another question for more details.
I think your type of the table2
private tb2 cd;
should be changed as
private CD cd;

Categories