I have the following entities:
____________________ ____________________
| Activity | | Benefit |
------------------ | |------------------|
| activityId:long |-------------| benefitId: long |
| activity:varchar | | activityId: long |
| .... | | benefit: varchar |
-------------------- -------------------|
Can I map this into Hibernate so I end up with this:
#Entity
class Activity {
#Id
private Long id;
private String activity;
private List<String> benefits;
}
Yes, you can use the #ElementCollection tag.
Here's what your code would look like:
#Entity
#Table(name = "Activity")
class Activity {
#Id
#Column(name="activity_id")
private Long id;
#Column(name = "name")
private String activity;
#ElementCollection
#CollectionTable(
name = "Benefit",
joinColumns = #JoinColumn(name = "activityId")
)
private List<String> benefits;
}
Though I would call the table ActivitiesBenefits instead of Benefit to make it clear that that table will store pairs of activities and benefits. Also, there is no need for a benefitId, since a benefit is a weak entity (it cannot exist without an activity), so you can drop that too.
Reference: https://en.wikibooks.org/wiki/Java_Persistence/ElementCollection
Hi I could find one way in which you could do it. Below is code for same.
#Entity
#Table(name = "Activity")
class Activity {
#Id
#Column(name="activity_id")
private Long id;
#Column(name = "name")
private String activity;
#OneToMany
#Formula("(select CONCAT(benefitId, activityId, benefit) from Benefit b where b.activityId = activity_id)")
private List<String> benefits;
}
I am using a sql returning list of benefits and then concatenate it and store it in our benefits list.
The string in formula is SQL(not HQL) so its column names and not filed member names.
Related
I have an entity like this:
#Entity
public Asset extends BaseEntity {
private String name;
private Localization currentLocalization;
private Localization plannedLocalization;
}
It throws Basic attribute type should not be 'Persistence Entity'.
I know that Entity should have Id etc, but what if I dont want to create another table, service, repository just for Localization who should be just a property, not another table.
Edit:
Localization:
#Embeddable
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class Localization {
#OneToOne(targetEntity = Floor.class, fetch = FetchType.LAZY)
#JoinColumn(name = "FLOOR_ID")
private Floor floor;
#Min(0)
#Max(1000)
private int xAxis;
#Min(0)
#Max(2400)
private int yAxis;
#Min(0)
#Max(999)
private int zAxis;
}
Here I am using #Embedded with Attribute Overrides like:
#Embedded
private Localization localization;
#Embedded
#AttributeOverrides({
#AttributeOverride(name="floor.id", column = #Column(name = "floor_plannedId"))
})
private Localization localizationPlanned;
but it throws:
Repeated column in mapping for entity: com.mrfisherman.relice.Entity.Asset.AssetEntity column: floor_id (should be mapped with insert="false" update="false")
No matter how I set name in #AttributeOverride
The error is due that Localization is not of any "basic type" that is directly mappable to any database column type. So it should either be an entity and fields of type Localization mapped with #OneToOne or #ManyToOne.
But you do not want another entity so the other option is to make it #Embeddable.
Assume your Localization is like:
#Getter #Setter
public class Localization {
private String str;
private Integer num;
}
You can flatten fields inLocalization to the containing class by annotating it like:
#Embeddable
public class Localization { ...
and in your Asset tell that this field should be embedded:
#Embedded
private Localization currentLocalization;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "str", column = #Column(name = "str2")),
#AttributeOverride(name = "num", column = #Column(name = "num2"))
})
private Localization plannedLocalization;
This would result into a table like:
Table "public.asset"
Column | Type | Collation | Nullable | Default
--------+------------------------+-----------+----------+---------
id | bigint | | not null |
num | integer | | |
str | character varying(255) | | |
name | character varying(255) | | |
num2 | integer | | |
str2 | character varying(255) | | |
As you see in the table, it is now flatten. And you also see that because there is two Localization in your Asset you need to do something with the clashing column names.
currentLocalization can use default naming but plannedLocalization cannot because currentLocalization already reserved those column names. So that is why there is a need for attribute override.
Considering all this you might evaluate again whether you create yet another entity and use #OneTOne or #ManyToOne mappings. It depends how compled your Localization is.
I've got two tables in my database that look like this, with a foreign key from job_label.job_id to equivalent column in job_record. Additionally, the triple of job_id, label_key, and label in job_record has a unique constraint.
mysql> select * from job_record;
+--------+---------+
| job_id | state |
+--------+---------+
| 1 | success |
| 2 | running |
| 3 | errored |
| 4 | success |
+--------+---------+
mysql> select * from job_label
+--------+-----------+--------+
| job_id | label_key | label |
+--------+-----------+--------+
| 1 | name | job 1 |
| 1 | type | normal |
+--------+-----------+--------+
On the Java class side I have this class:
#Entity
#Table(name = "job_record")
public class JobRecord {
#Id
#Column(name = "job_id")
private String jobId;
#Enumerated(EnumType.STRING)
#Column(name = "state")
private JobState state;
}
I've tried to define a class for job_label that looks something like this:
public class JobLabelRecord {
#Enumerated(EnumType.STRING)
#Column(name = "label_key")
private JobLabelKey key;
#Column(name = "label")
private String label;
}
And then I want a field in JobRecord that provides me all labels for that Job as a List<JobLabelRecord>. However, nothing I've tried works.
I've tried declaring JobLabelRecord as Embeddable with the equivalent field in JobRecord as Embedded. I've tried using ManyToOne and OneToMany mappings, but that fails because there's no Id columns in JobLabelRecord (and nothing I do with those works correctly).
Am I supposed to be using an ElementCollection here instead? I've tried that as well, but without success.
Any ideas here? Thanks!
You can find a simple example for this by searching for "jpa elementcollection separate table", such as JPA Tutorial - JPA ElementCollection CollectionTable Override Example. Applying this example to your problem gives the following:
#Entity
#Table(name = "job_record")
public class JobRecord {
#Id
#Column(name = "job_id")
private Integer jobId;
#Enumerated(EnumType.STRING)
#Column(name = "state")
private JobState state;
#ElementCollection
#CollectionTable(name="job_label",
joinColumns=#JoinColumn(name="job_id"))
private List<JobLabelRecord> labels;
...
#Embeddable
public class JobLabelRecord {
#Enumerated(EnumType.STRING)
#Column(name = "label_key")
private JobLabelKey key;
#Column(name = "label")
private String label;
...
Note also the JobRecord id should probably be an Integer.
I have two existing, highly normalized, tables (Activity and Status)
Create table Activity (
id Number(10,0) not null,
description varchar2(4000) not null,
create_date date not null
);
Create table Status (
table_name varchar2(20) not null,
record_id number(10,0) not null,
status_description varchar2(4000)
);
The Status table relates, in this case, to the Activity table by the following:
STATUS.TABLE_NAME = 'Activity' and STATUS.RECORD_ID = ACTIVITY.ID
It can relate to many other tables as well (besides ACTIVITY):
STATUS.TABLE_NAME = 'Scores' and STATUS.RECORD_ID = SCORE.ID
STATUS.TABLE_NAME = 'Submissions' and STATUS.RECORD_ID = submission.ID
STATUS.TABLE_NAME = 'Tickets' and STATUS.RECORD_ID = TICKET.ID
STATUS.TABLE_NAME = 'Profiles' and STATUS.RECORD_ID = Profile.ID
- STATUS TABLE -
table_name | record_id | status_decription
----------- | ----------- | -----------
'Activity' | **12** | 'Finished'
'Profiles' | 100 | 'Completed'
'Scores' | 200 | 'Calculated'
'Tickets' | 1000 | 'Paid'
- ACTIVITY TABLE -
id | description
----------- | -------------
10 | blah, blah
11 | hey there..
**12** | order pizza
13 | pick up icecream
So given the previous example tables, there was an activity where an individual "'Finished' ordering his pizza"
I am trying to create this relationship with Hibernate, however I cannot seem to figure out the mapping between these two classes.
#Entity(name="status")
#Table(name="Status")
public class StatusDb {
#Column(name="table_name")
private String tableName;
#Column(name="record_id")
private String recordId;
#Column(name="status_desc")
private String description;
// setters/getters
// equals/hashCode
}
#Entity(name="actvity")
#Table(name="Activity")
public class ActivityDb {
#Column(name="id")
#Id
private Long id;
#Column(name="description")
private String description;
// setters/getters
// equals/hashCode
}
How can I relate this #OneToOne mapping between the Status table and the Activity,Profiles,Tickets,Workflow tables?
I assume that the ActivityDb is the 'parent' of the relationship.
I would map my entities as follows:
StatusDb
#Entity(name="status")
#Table(name="Status")
public class StatusDb {
#Column(name="table_name")
private String tableName;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula=#JoinFormula(value="tableName = 'Activity'"),
#JoinColumnOrFormula(column = #JoinColumn("record_id",
referencedColumnName="id"))
})
private ActivityDb activityDb;
#Id
#Column(name="record_id")
private String recordId;
#Column(name="status_desc")
private String description;
// setters/getters
// equals/hashCode
}
ActivityDb
#Entity(name="actvity")
#Table(name="Activity")
public class ActivityDb {
#Column(name="id")
#Id
private Long id;
#Column(name="description")
private String description;
#OneToOne(mappedBy = "activityDb")
private StatusDb statusDb;
}
Now when the id is determined for the ActivityDb entity, upon commit of the transaction, the related StatusDb entity will automatically have the record_id populated with that ActivityDb's id.
Here's the solution that worked for me
#Entity(name="status")
#Table(name="Status")
public class StatusDb {
#Column(name="table_name")
private String tableName;
#Column(name="record_id")
private String recordId;
#Column(name="status_desc")
private String description;
// setters/getters
// equals/hashCode
}
#Entity(name="actvity")
#Table(name="Activity")
public class ActivityDb {
#Column(name="id")
#Id
private Long id;
#Column(name="description")
private String description;
#OneToMany
#Where(value="table_name='Activity'")
#JoinTable(name="STATUS", joinColumn=#JoinColumn(name="id"),
reverseJoinColumn=#JoinColumn(name="record_id")
private Set<StatusDb> status; // Had to to make 1:M to use where clause
// setters/getters
// equals/hashCode
}
I have a problem using JPA.
I have to tables:
-----------------
| TableA |
|---------------|
| ID: INT |
| ... |
| ESTATUS1: INT |
| ESTATUS2: INT |
-----------------
-----------------
| EstatusTags |
|---------------|
| COD: VARCHAR |---> COD and VALUE are a concatenated PK
| VALUE: INT |
| DESC: VARCHAR |
-----------------
EstatusTags is a table to store sets of pairs [VALUE, DESC], given a COD.
Before I use JPA, I used to query this kind of data in something like this:
SELECT ID, ESTATUS1, ESTATUS2, E1.DESC DESC1, E2.DESC DESC2
FROM TABLEA A
INNER JOIN ESTATUSTAGS E1 ON E1.COD = "a_estatus1"
AND E1.VALUE = A.ESTATUS1
INNER JOIN ESTATUSTAGS E2 ON E2.COD = "a_estatus2"
AND E2.VALUE = A.ESTATUS2
I'm trying to use JPA to model this using two entity classes:
#Entity
#Table(name = "EstatusTags")
public class EstatusTags implements Serializable {
#EmbeddedId
private ValueTagPK id;
#Column(name="VVA_DESC")
private String desc;
#Column(name="VVA_ORDEN")
private Integer orden;
}
#Entity
#Table(name = "TableA")
public class A implements Serializable {
#Column(name="ID")
private String desc;
#OneToOne(???)
private EstatusTag estatus1;
#OneToOne(???)
private EstatusTag estatus2;
}
I have strong doubts in how to model the relations. Can it be done with annotations? There is necesary the JPQL use to fit this structure?
I hope somebody could help me with this.
Thanks a lot.
The problem is that your entity model does not match the table structure.
In your entity model you have a one to one relation ship between A and EstatusTag whereas in your table model you have a relationship of one A and multiple Estatustags (for one value there may exist multiple Etatustags entries)
You overcome the problem that Table A does not have a cod column by adding something like a virtual cod column E1.COD = "a_estatus1" to your SQL Query.
What you can do is you map the value column of to two properties of EstatusTag one time to the composite pk and the other time to a single property in the following way . The simple value is made accessible via property access but marked as not updatable not insertable also the setter does not really work and is made private.
Remark: I don't know if that works with all JPA implementations - Tested with hibernate 4.3.8.
#Entity
#Table(name = "EstatusTags" )
#Access(AccessType.FIELD)
public class EstatusTag implements Serializable{
private #EmbeddedId ValueTagPK id;
#Column(name="VVA_DESC")
private String desc;
#Column(name="VVA_ORDEN")
private Integer orden;
#Column(name="value", updatable=false, insertable=false)
#Access(AccessType.PROPERTY)
public int getValue() {
return id.value;
}
private void setValue(int value) {
// only because otherwise hibernate complains about a missing setter.
}
}
#Entity
#Table(name = "TableA")
public class A implements Serializable{
#Id
#Column(name="ID")
#GeneratedValue(strategy=GenerationType.TABLE)
private int id;
#OneToOne()
#JoinColumn(name="estatus1",referencedColumnName="value")
public EstatusTag estatus1;
#OneToOne()
#JoinColumn(name="estatus2",referencedColumnName="value")
public EstatusTag estatus2;
}
Hibernate UnUniqueify a column in table(Solved)
I want a field set to be non-unique on itself but to be unique in combination with the other field, I got this table with two columns(composite primary keys); id (primary key) and object_proxy_id (primary key), this is exactly what I need but hibernate sets the object_proxy_id to be unique on itself so that value cant be duplicate in the table, and I need this column to accept duplicate values. Because every user has its own object proxy and these proxy's don't have to be necessarily unique.
This is what I want to achieve:
|-------------------------------|
| tbl_object_proxy |
| ------------------------------|
| Id (pk)| object_proxy_id (pk) |
|-------------------------------|
| 1 | 150 -- |
| 1 | 149 |= must be able to be DUPLICATE which is not the case right now.
| 2 | 150 -- |
| 2 | 151 |
|-------------------------------|
Current code:
#Entity
#Table(name = "tbl_user_settings", uniqueConstraints = {#UniqueConstraint(columnNames={"user_id"})})
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Settings implements Serializable
{
#Id
#SequenceGenerator(name="someSequence", sequenceName="SEQ_SOMENAME", allocationSize =1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="someSequence")
#Column(name="id")
private int setting_id;
#OneToOne
private User user;
#ManyToOne
private SomeObject someobject;
#ElementCollection
#CollectionTable(name="tbl_collection_name", joinColumns=
#JoinColumn(name="id"), uniqueConstraints = {#UniqueConstraint(columnNames={"id", "object_proxy_id"})})
#Column(name="SomeObject")
private Set<SomeObject> objectProxy;
/*...constructors and methods...*/
}
Results in:
-- Table schema
|-------------------|
| tbl_user_settings |
|-------------------|
| id |PK <<Unique>>
| user_id |FK reference tbl_user <<Unique>>
| object_id |FK reference tbl_object
|-------------------|
|------------------|
| tbl_object_proxy |
|------------------|
| id |PK reference tbl_user_settings
| object_proxy_id |PK reference tbl_object <<Unique>> BUT I DON'T WANT THIS TO BE UNIQUE ON ITSELF !!!!
|------------------|
EDIT:
The two primary key's in tbl_object_proxy are composite primary key's
I have tried Xeon's solution but it didn't work.
Short answer: replace the #ElementCollection by a #ManyToMany relation with a #JoinTable like this:
#ManyToMany
#JoinTable(
name="tbl_settings_objecteproxy_v2",
joinColumns = #JoinColumn(name = "id"),
inverseJoinColumns = #JoinColumn( name = "objectproxy_id"))
private Set<SomeObject> objectproxy;
See "2.2.5.3.2.1. Definition" in Hibernate Annotation Documentation
This results in a same side table but then without the unique constraint.
So now this is possible:
|-------------------------------|
| tbl_object_proxy |
| ------------------------------|
| Id (pk)| object_proxy_id (pk) |
|-------------------------------|
| 1 | 150 -- |
| 1 | 149 |= It works! The unique constraint is gone!
| 2 | 150 -- |
| 2 | 151 |
|-------------------------------|
Detailed answer and cause description:
Somehow the #ElementCollection created a collectiontable with a one to many relation of the referenced key (collection | inverse join) which adds a unique constraint to the key referencing the other side table to reflect the one to many relationship which I didn't want. So I dropped the #ElementCollection and replaced it by a #ManyToMany relation with a #JoinTable annotation. I have also tried to declare the #ManyToMany relation in the #ElementCollection but it kept adding the Unique constraint to the referenced key.
My Settings class does now look like this:
#Entity
#Table(name = "tbl_user_settings", uniqueConstraints = {#UniqueConstraint(columnNames={"user_id"})})
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Settings
{
#Id
#SequenceGenerator(name="someSequence", sequenceName="SEQ_SOMENAME", allocationSize =1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="someSequence")
#Column(name="id")
private int setting_id;
#OneToOne
private User user;
#ManyToOne
private SomeObject someobject;
#ManyToMany
#JoinTable(
name="tbl_settings_objecteproxy_v2",
joinColumns = #JoinColumn(name = "id"),
inverseJoinColumns = #JoinColumn( name = "objectproxy_id"))
private Set<SomeObject> objectProxy;
/*...the rest...*/
}
I had similar issue with articles and categories:
public class ArticleCategoriesEntity {
#EmbeddedId
public ArticleCategoriesIdPk getArticleCategoriesIdPk() {
return articleCategoriesIdPk;
}
public void setArticleCategoriesIdPk(ArticleCategoriesIdPk articleCategoriesIdPk) {
this.articleCategoriesIdPk = articleCategoriesIdPk;
}
...
#Embeddable
public class ArticleCategoriesIdPk implements Serializable {
public ArticleCategoriesIdPk() {
}
public ArticleCategoriesIdPk(Integer articleCategoryIdPk, Integer articleCategoryVersionFk) {
this.articleCategoryIdPk = articleCategoryIdPk;
this.articleCategoryVersionFk = articleCategoryVersionFk;
}
private Integer articleCategoryIdPk;
#Column(name = "article_category_id_pk")
public Integer getArticleCategoryIdPk() {
return articleCategoryIdPk;
}
public void setArticleCategoryIdPk(Integer articleCategoryIdPk) {
this.articleCategoryIdPk = articleCategoryIdPk;
}
private Integer articleCategoryVersionFk;
#Column(name = "article_cat_version_fk")
public Integer getArticleCategoryVersionFk() {
return articleCategoryVersionFk;
}
public void setArticleCategoryVersionFk(Integer articleCategoryVersionFk) {
this.articleCategoryVersionFk = articleCategoryVersionFk;
}
And you need to set Uniqueness of the two columns of embedded PK class