To the existing table through #ManyToOne
#ManyToOne
#JoinColumns({
#JoinColumn(name = "ID", referencedColumnName = "APPLICATION_ID", insertable = false, updatable = false),
#JoinColumn(name = "VERSION", referencedColumnName = "APPLICATION_VERSION", insertable = false, updatable = false)
})
private ApplicationsBodies applicationsBodies;
I join another table.
But from the join table, I want to join only one column.
#Entity
#Table
public class ApplicationsBodies implements Serializable {
...
#Column(name = "APPLICATION_ID")
private Long applicationId;
#Column(name = "APPLICATION_VERSION")
private Long applicationVersion;
//I want to attach only this column
#Lob
#Column(name = "BODY")
private String body;
#Column(name = "ACTIVE_STATE")
private Integer activeState;
How can this be implemented using JPA / Hibernate?
UPDATE: Solution
My problem was solved by #Formula annotation. Because When I refer to an entity only for the purpose of loading this one field, for me it has become the most optimal solution.
I deleted the field: private ApplicationsBodies applicationsBodies. And created a field: private String body with annotation #Formula with value - SQL query joining only one column.
if you want to use join with JPA data, then I recommend to use JPL Query Language. So in your repository classes use as annotation :
#Query("select a.name from ApplicationsBodies a join a.applicationId d where d.id = :applicationVersion")
Related
I have two entities:
#Entity
#Table(name = "entity_a")
#Audited
public class EntityA {
#Column(name = "entity_a_uuid", columnDefinition = "char", updatable = false)
#Type(type = "uuid-char")
private UUID uuid = UUID.randomUUID();
/**
* #deprecated in favor of uuid
*/
#Deprecated
#Id
#GeneratedValue
#Column(name = "entity_a_id")
private Integer id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "entity_a_id", nullable = false)
#BatchSize(size = 100)
#NotAudited
private List<EntityB> entityBs = new ArrayList<>();
}
and
#Entity
#Audited
#Table(name = "entity_b")
public class EntityB {
#Id
#Column(name = "entity_b_uuid", columnDefinition = "char", updatable = false)
#Type(type = "uuid-char")
private UUID uuid = UUID.randomUUID();
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "entity_a_id", nullable = false, insertable = false, updatable = false)
private EntityA entityA;
}
Each is correctly audited into two tables entity_a_audit and entity_b_audit. However, the entity_a_id field in entity_b_audit is always null.
Some details:
If I do not have the #NotAudited in EntityA, I will get an error that says something to the effect of: The table EntityA_EntityB_audit does not exist. This seems like it's trying to audit them as a single table, which I do not want.
I have tried applying #Audited(targetAuditMode = elationTargetAuditMode.NOT_AUDITED) to each side. If applied only in EntityA, I get the above error. If applied only in EntityB, nothing changes. If applied in both, I get the error above. If applied to neither, I get the error above.
I suspect the entity_a_id is null in entity_b_audit because the id isn't generated until EntityA hits the DB. entity_a_id is auto-incrementing in the entity_a table.
Using hibernate-envers-5.4.32.Final.jar
Ultimately, I would like for entity_a_id to not be null in entity_b_audit. Alternatively, if I could somehow get entity_a_uuid to be captured instead, that would also suffice.
Any help would be greatly appreciated! Thanks.
You marked the column as insertable = false, updatable = false, so there is nothing to audit here, because Hibernate can never change the value of that column.
I have an old database where can't change the schema so I have to adapt my code to work.
Within that db I have two tables which I'd like to join.
The main Table called SFTSEB2 has a 5 Column Composite primary key: empfkey, ebmsg, ebbord, ebsort, ebrefn only these will going to fulfill the uniqueness on that Table.
I'd like to join SFTSEB3 to SFTSEB2 but based on only 4 Columns: empfkey, ebmsg, ebbord, ebsort
Entites:
#Entity
#Table(name = "SFTSEB2")
#IdClass(Sftseb2ID.class)
#Immutable
public class Sftseb2 {
#Id
private String empfkey;
#Id
private String ebmsg;
#Id
private String ebbord;
#Id
private int ebsort;
private int ebbpos;
private String ebsdgn;
#Id
private String ebrefn;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "empfkey", referencedColumnName = "empfkey", insertable = false, updatable = false),
#JoinColumn(name = "ebmsg", referencedColumnName = "ebmsg", insertable = false, updatable = false),
#JoinColumn(name = "ebbord", referencedColumnName = "ebbord", insertable = false, updatable = false),
#JoinColumn(name = "ebsort", referencedColumnName = "ebsort", insertable = false, updatable = false)
})
List<Sftseb3> sftseb3;
//GettersSetters
...
}
#Entity
#Table(name = "SFTSEB3")
#IdClass(Sftseb3ID.class)
#Immutable
public class Sftseb3 {
#Id
private String empfkey;
#Id
private String ebmsg;
#Id
private String ebbord;
#Id
private int ebsort;
#Id
private int ebsrt2;
#Id
private String ebsdgn;
private int ebbpos;
private String ebstst;
private String ebstsc;
//GettersSetters
//...
}
Unfortunately I can get past this because hibernate is giving back:
A Foreign key refering Sftseb2 from Sftseb3 has the wrong number of column. should be 5
I tried to have a bi-directional mapping so the complete JoinColumns part went to Sftseb3 with a #ManyToOne and Sftseb2 would only contain the mappedBy at the #OneToMany but I got the same issue.
As I see there is no way to have different PK on an entity and Join another table to it based on part of the PK or even other columns? Why?
If I reduce the number of #Id columns to match in the two tables so Sftseb2's #Ids: empfkey, ebmsg, ebbord, ebsort then it starts to work, however because this is non-unique all instances of Sftseb2 will be the same...
I want to specify join type on two hibernate entities
`
#Entity
#Table(name = "b_template_control")
public class TemplateControl {
#Column(name = "id")
private Long id;
#JsonInclude(JsonInclude.Include.NON_EMPTY)
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.DETACH)
#JoinColumn(name = "back_validation_id", nullable = false, insertable
= false, updatable = false)
private ValidationRuleBack validationRuleBack;
}
`
as you can see we have #ManyToOne relation, by default hibernate creates INNER JOIN query but I need LEFT JOIN. The question is how could I specify join type using annotations.
Answer
It is nullable = false, so how would a left join be different from an inner join?
Please refer the following link.
https://docs.jboss.org/hibernate/core/3.3/api/org/hibernate/FetchMode.html
The join type FetchMode implies, the query will be using an outer join
I have got the following Entities, an item which can has up to two categories, a primary and a secondary.
Both categories are mapped ManyToOne to the category table using a JoinColumnsOrFormulas.
The first one gets fetched EAGER as expected, but the second one does not occur in the SQL statement and gets lazy loaded.
This lazy loading results in a classical n+1 problem.
This is my item entity with the both category entities which should gets joined:
#Entity
#Table(name = "item", schema = "public", catalog = "stackoverflow_question")
#DynamicUpdate
public class Item extends StackOverflowQuestionEntity {
#Id
#Column(name = "id")
protected Long id;
#Column(name = "site")
private String site;
#ManyToOne
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value = "site", referencedColumnName = "site")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "primary_category_id", referencedColumnName = "category_id"))
})
private Category primaryCategory;
#Column(name = "primary_category_id")
private Long primaryCategoryId;
#ManyToOne
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value = "site", referencedColumnName = "site")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "secondary_category_id", referencedColumnName = "category_id"))
})
private Category secondaryCategory;
#Column(name = "secondary_category_id")
private Long secondaryCategoryId;
}
This is the category entity:
#Entity
#Table(name = "category", schema = "public", catalog = "stackoverflow_question")
public class Category extends StackOverflowQuestionEntity {
#Column(name = "category_id")
private Long categoryId;
#Column(name = "name")
private String name;
#Column(name = "site")
private String site;
}
The resulting query contains only the primary category:
SELECT this_.id AS id1_9_9_,
this_.inserted AS inserted2_9_9_,
this_.updated AS updated3_9_9_,
this_.primary_category_id AS formula174_9_,
this_.secondary_category_id AS formula176_9_,
category2_.id AS id1_0_0_,
category2_.inserted AS inserted2_0_0_,
category2_.updated AS updated3_0_0_,
category2_.name AS name7_0_0_
FROM public.item this_
LEFT OUTER JOIN public.category category2_ ON this_.site=category2_.site
AND this_.primary_category_id=category2_.category_id
WHERE True;
Hence the secondary category get joined lazy:
SELECT category0_.id AS id1_0_0_,
category0_.inserted AS inserted2_0_0_,
category0_.updated AS updated3_0_0_,
category0_.name AS name4_0_0_,
category0_.site AS site5_0_0_
FROM public.category category0_
WHERE category0_.site=?
AND category0_.category_id=?;
Why is Hibernate joining the secondary category lazy, the annotations seems the be the same.
The hibernate version I am using is 5.0.10.Final.
This is how the base entity looks like:
#MappedSuperclass
abstract public class StackOverflowQuestionEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, insertable = true, updatable = false, nullable = false)
protected Long id;
#Type(type="LocalDateTime")
#Column(name = "created", nullable = false, insertable = true, updatable = false)
protected LocalDateTime created;
#Type(type="LocalDateTime")
#Column(name = "refreshed", nullable = false, insertable = true, updatable = true)
protected LocalDateTime refreshed;
#PreUpdate
protected void onUpdate() {
refreshed = now();
}
#PrePersist
protected void onCreate() {
created = refreshed = now();
}
}
Here is an example "query", as said I am using hibernate criteria as well as HQL, the problem occurs with both methods.
session
.createCriteria(Item.class)
.add(eq("id", id))
.uniqueResult();
With standard JPA annotations it would look like this (updated):
#ManyToOne
#JoinColumns({
#JoinColumn(name="site", referencedColumnName="site", insertable = false, updatable = false),
#JoinColumn(name="primary_category_id", referencedColumnName="category_id", insertable = false, updatable = false)
})
private Category primaryCategory;
#ManyToOne
#JoinColumns({
#JoinColumn(name="site", referencedColumnName="site", insertable = false, updatable = false),
#JoinColumn(name="secondary_category_id", referencedColumnName="category_id", insertable = false, updatable = false)
})
private Category secondaryCategory;
UPDATE: I found that the second select statement is generated only when you use join by a composite key: Hibernate tries to resolve associations for {site=site, id=null} using TwoPhaseLoad. But if you write
#ManyToOne
#JoinColumn(name="secondary_category_id")
private Category secondaryCategory;
and secondary_category_id is null then the only one select statement will be generated, and the secondaryCategory value will be null. Maybe it will help you somehow. For example, you could add a constraint on site field while building your criteria:
Category c = (Category) session.createCriteria(Category.class)
.add(Restrictions.eq("id", 1L)) // for example
// here you define additional restriction on site field
.createAlias("secondaryCategory", "sc", JoinType.LEFT_OUTER_JOIN, Restrictions.sqlRestriction("this_.site = {alias}.site"))
.uniqueResult();
I did a quick test using your classes, and the following query code (using JPA criteria queries rather than native Hibernate)
CriteriaQuery<Item> cq = em.getCriteriaBuilder().createQuery(Item.class);
EntityGraph<Item> entityGraph = em.createEntityGraph(Item.class);
entityGraph.addSubgraph("primaryCategory", Category.class);
entityGraph.addSubgraph("secondaryCategory", Category.class);
List<Item> items = em.createQuery(cq.select(cq.from(Item.class)))
.setHint("javax.persistence.loadgraph", entityGraph)
.getResultList();
results in the following SQL being generated (formatted for readability):
select item0_.id as id1_1_0_,
category1_.id as id1_0_1_,
category2_.id as id1_0_2_,
item0_.site as site4_1_0_,
item0_.primary_category_id as primary_2_1_0_,
item0_.secondary_category_id as secondar3_1_0_,
category1_.category_id as category2_0_1_,
category1_.name as name3_0_1_,
category1_.site as site4_0_1_,
category2_.category_id as category2_0_2_,
category2_.name as name3_0_2_,
category2_.site as site4_0_2_
from item item0_
left outer join category category1_
on item0_.site=category1_.site
and item0_.secondary_category_id=category1_.category_id
left outer join category category2_
on item0_.site=category2_.site
and item0_.primary_category_id=category2_.category_id
As you can see, both category tables are being joined in the same SELECT
Try the following solution:
#Entity
#Table(name = "item", schema = "public", catalog = "stackoverflow_question")
#DynamicUpdate
public class Item {
#ManyToOne
#JoinColumn(name="site")
private Category primaryCategory;
#ManyToOne
#JoinColumn(name="site")
private Category secondaryCategory;
}
#Entity
#Table(name = "category", schema = "public", catalog = "stackoverflow_question")
public class Category {
#OneToMany(targetEntity=Item.class, mappedBy="primaryCategory", cascade=CascadeType.ALL)
private List<Item> primaryCategoryList;
#OneToMany(targetEntity=Item.class, mappedBy="secondaryCategory", cascade=CascadeType.ALL)
private List<Item> secondaryCategoryList;
}
I have 2 entities in hibernate A and B. Here is the relevant code.
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#OneToOne(mappedBy = "a", cascade = CascadeType.ALL)
private B b;
}
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#Column(name = "a_id")
#GeneratedValue(generator = "gen")
#GenericGenerator(name = "gen", strategy = "foreign", parameters = #Parameter(name = "property", value = "a"))
private Integer aId;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#PrimaryKeyJoinColumn
private A a;
}
I did the same as mentioned in the below mentioned link
one to one mapping using primary key join column
However, when I do the following hql query,
"from A a left join a.b"
the join is taken on the following condition
a.id = b.id
although what I desire is the following condition
a.id = b.aId
You must use #JoinColumn(name = "a_id") instead of #PrimaryKeyJoinColumn.
By the way, you can't define two fields on the same column. However, if you need to do so you must make one of them not insertable and not updateable like this:
#JoinColumn(name = "a_id", insertable = false, updatable = false)
You have given the reference of class A to field a in class B.
#OneToOne(fetch = FetchType.LAZY, optional = false)
#PrimaryKeyJoinColumn
private A a;
And class B to field b in class A
#OneToOne(mappedBy="a", cascade=CascadeType.ALL)
private B b;
so by default Hibernate create a join query with referenced field. So hibernate by default perform the join on a.id = b.id.
But i think you can create your own query and use native query for performing join with a.id = b.aId.