Single table inheritance and relationship - java

I'm trying to find a solution for a this problem:
We have a table (code_list) in the database which contains all enum -like data.
Let us say we have an Affiliate that can have an AffiliateType and a LanguageCode.
We put all this in the code_list table where id_code_list field tells us if we are talking about AffiliateType or Languagecode and we can have a String or Integer identifier that tells us about which AffiliateType we are talking.
Example of the data in table:
| id_code_list | val_num | val_string | label |
| :----------: |:------: |:---------: | :-----:|
| TYP_AFF | 3 | 3 | Other |
| TYP_AFF | 1 | 1 | Divers |
| COD_LAN | 1 | 1 | French |
I tried to map this that way:
Code List Parent
#Entity
#Table(name = "CODE_LIST")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "ID_CODE_LIST")
public abstract class CodeListString {
#Id
#Column(name = "VAL_STRING")
protected String value;
#Embedded
protected Label label;
...
Affiliate Type:
#Entity
#DiscriminatorValue("TYP_AFF")
public class AffiliateType extends CodeListString{
public static final AffiliateType SOCIAL_SECRETARIAT = new AffiliateType("1");
public static final AffiliateType VARIOUS_SERVICES = new AffiliateType("2");
public static final AffiliateType OTHERS = new AffiliateType("3");
public static final AffiliateType SOPA = new AffiliateType("9");
public AffiliateType() {}
private AffiliateType(String value) {
super(value);
}
}
And my Affiliate entity:
#Entity
#Table(name = "AFF")
public class Affiliate {
#ManyToOne
#JoinColumn(name = "TYP_AFF")
private AffiliateType type;
But I'm getting this error:
org.hibernate.MappingException: Foreign key (FK7re97tvvbbo2km961gy9b5jw6:aff [typ_aff])) must have same number of columns as the referenced primary key (code_list [val_string,id_code_list])
So, is there a way to make this work or do you have other solution for this problem ?
PS: I'm using Hibernate and the solution like
#ManyToOne(targetEntity = AffiliateType.class)
#JoinColumn(name = "TYP_AFF")
#Where(clause = "ID_CODE_LIST='TYP_AFF'")
private AffiliateType type;
Doesn't work...

I've removed a dependency to a custom library and this worked.
I could not reproduce this error on a clean project.
Anyway this solution is really slow !

Related

How to fetch data from multiple tables in spring boot using mapping in Spring Boot's JPA repository

I have created four entity classes as:
#Entity
#Table(name = "DashboardRegionCountry")
public class DashboardRegionCountry implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "dashboardId")
private long dashboardId;
#OneToOne(targetEntity = Country.class)
#JoinColumn(name="countryId")
private Country country;
#OneToOne(targetEntity = Region.class)
#JoinColumn(name="regionId")
private Region region;
#ManyToOne()
#JoinColumn(name="dashboardId")
private Dashboard dashboard;
}
#Entity
#Table(name = "Dashboard")
public class Dashboard implements Serializable {
#Id
#Column(name = "dashboardId")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long dashboardId;
#Column(name = "dashboardName")
private long dashboardName;
#OneToMany(mappedBy= dashboard)
private List<DashboardRegionCountry> dashboardRegionCountry;
}
#Entity
#Table(name = "Country")
public class Country implements Serializable {
#Id
#Column(name = "countryId")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long countryId;
#Column(name = "shortName")
private String shortName;
#Column(name = "longName")
private String longName;
}
#Entity
#Table(name = "Region")
public class Region implements Serializable {
#Id
#Column(name = "regionId")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long regionId;
#Column(name = "shortName")
private String shortName;
#Column(name = "longName")
private String longName;
}
And the table schemas for the respective entities are as follows:
DashboardRegionCountry:
+----------------+---------------+
| Field | Type |
+----------------+---------------+
| id(PK) | Number(11) |
| dashboardId(FK)| Number(11) |
| countryId | Number(11) |
| regionId | Number(11) |
+-------------+------------------+
Dashboard:
+----------------+---------------+
| Field | Type |
+----------------+---------------+
| dashboardId(PK)| Number(11) |
| dashboardName | varchar(11) |
+-------------+------------------+
Country:
+-------------+---------------+
| Field | Type |
+-------------+---------------+
| countryId(PK)| Number(11) |
| shortName | Varchar2(10) |
| longName | Varchar2(10) |
+-------------+---------------+
Region:
+-------------+---------------+
| Field | Type |
+-------------+---------------+
| regionId(PK)| Number(11) |
| shortName | Varchar2(10 |
| longName | Varchar2(10) |
+-------------+---------------+
Basically, when user enters the dashboardId then we want to fetch, dashboardDetails along with the Region and respective countries presnt in that region. As stated above, I only have region and country Ids in my table and their names are present in other tables.
I want to display my sample output something like:
{
"dashboardId":20,
"DashboardRegionCountry": [{
"Region":"ASIA",
"dashboardId":["India","China"]
},
{
"Region":"NAM",
"dashboardId":["USA","Canada"]
}
]
}
I am trying to write JPA repository but was wondering is it possible to write something like:
#Repository
public interface DashboardRegionCountryRepository extends JpaRepository<DashboardRegionCountry, Long>{
List<Map<Long,Country>> findRegionBy_RegionId(Long dashboardId);
}
I am trying to fetch all the data in one query, any suggestion will be really helpful
Just get the corresponding DashboardRegionCountry using getById (check the reference documentation) and it will contain both associated Country and Region. If you then don't want to expose all Country and Region information in your entities I suggest you map them to a DTO that would be the model that you want to use while returning something on your controllers.
findByRegion_regionId try this this will work.

Is there any way to set String property of class depending on tablename?

I have two tables in my database, at "retail" schema, with indent column sets:
"vegetable_group":
+------------------+--------------------+
| (varchar) id | (varchar) value |
+------------------+--------------------+
| 1 | good_vegetables |
| 2 | bad_vegetables |
+------------------+--------------------+
"fruit_group":
+------------------+--------------------+
| (varchar) id | (varchar) value |
+------------------+--------------------+
| 3 | good_fruit |
| 4 | bad_fruit |
+------------------+--------------------+
also i have an enum in database type product_type as enum ('fruit', 'vegetable');
this is my entity:
#Data
#Entity
#Table(schema = "retail", name = "vegetable_group")
#SecondaryTables({
#SecondaryTable(schema = "retail", name = "fruit_group",
pkJoinColumns = #PrimaryKeyJoinColumn(name = "id", referencedColumnName = "id")
)
})
public class Product implements Serializable {
private enum ProductType {
vegetable, fruit
}
#Id
#Column(name = "id")
private String id;
#Enumerated(EnumType.STRING)
ProductType productType;
#Column(name = "value")
private String value;
}
and simple ProductRepository
public interface ProductRepository extends JpaRepository<Product, String> {
}
I need to set somehow the value of product.productType depending on the table, from combined data...
Can i somehow simply do that?
Upd.
I'm using #SecondaryTable, because i need to get data from two tables using a single entity. For example, i have a Product with id 99. I don't know whether it is a vegetable or a fruit. The common approach for such process is to create two entities: Fruit and Vegetable and search them by id's. But database structure can't be changed so i use one entity Product, that gets data from both tables by unique id.

Composite table with Hibernate Envers

I have an application with a composite table holding one extra column. It all works fine, until we add Hibernate Envers (#Audited).
org.hibernate.MappingException: Unable to read the mapped by attribute for responseDomainCodes in no.pack.response.ResponseDomainCode
I am happy to provide more detailed information if necessary, however, at this time I am not sure what would be relevant.
The tables look like this, and is a pretty standard composite key table, with one extra column.
Database schema
+-----------+---------+
| CODE | TYPE |
+-----------+---------+
| category | VARCHAR |
| code | VARCHAR |
+-----------+---------+
|
|
+----------------------+---------+
| RESPONSE_DOMAIN_CODE | TYPE |
+----------------------+---------+
| response_domain_id | KEY |
| code_id | KEY |
| rank | VARCHAR |
+----------------------+---------+
|
|
+--------------------+------+
| RESPONSE_DOMAIN | TYPE |
+--------------------+------+
| response_domain_id | PK |
| response_kind_id | FK |
+--------------------+------+
ResponseDomain.java
#Entity
#Table(name = "responseDomain")
public class ResponseDomain implements Serializable {
#Id
#Column(name = "responseDomain_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "respons_kind_id")
private ResponseKind responseKind;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.responseDomain", cascade = CascadeType.ALL)
private Set<ResponseDomainCode> responseDomainCodes = new HashSet<>();
//Omitted rest.
}
Code.java
#Entity
#Table(name = "code")
public class Code implements Serializable {
#Id
#Column(name = "code_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String category;
private String code;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.code", cascade = CascadeType.ALL)
private Set<ResponseDomainCode> responseDomainCodes = new HashSet<>();
//Omitted rest
}
ResponseDomainCode.java
#Entity
#Table(name = "responseDomain_code")
#AssociationOverrides(value = {
#AssociationOverride(name = "pk.responseDomain",
joinColumns = #JoinColumn(name = "responseDomain_id")),
#AssociationOverride(name = "pk.code",
joinColumns = #JoinColumn(name = "code_id"))
})
public class ResponseDomainCode implements Serializable {
#EmbeddedId
private ResponseDomainCodeId pk = new ResponseDomainCodeId();
#Column(name = "rank")
private String rank;
public ResponseDomainCodeId getPk() {
return pk;
}
public void setPk(ResponseDomainCodeId pk) {
this.pk = pk;
}
public String getRank() {
return rank;
}
public void setRank(String rank) {
this.rank = rank;
}
#Transient
public ResponseDomain getResponseDomain() {
return getPk().getResponseDomain();
}
public void setResponseDomain(ResponseDomain responseDomain) {
this.getPk().setResponseDomain(responseDomain);
}
#Transient
public Code getCode() {
return getPk().getCode();
}
public void setCode(Code code) {
this.getPk().setCode(code);
}
//Omitted rest
}
ResponseDomainCodeId.java
#Embeddable
public class ResponseDomainCodeId implements Serializable {
#ManyToOne
private ResponseDomain responseDomain;
#ManyToOne
private Code code;
public ResponseDomainCodeId() {
}
public ResponseDomain getResponseDomain() {
return responseDomain;
}
public void setResponseDomain(ResponseDomain responseDomain) {
this.responseDomain = responseDomain;
}
public Code getCode() {
return code;
}
public void setCode(Code code) {
this.code = code;
}
//Omitted rest
}
With the help of #adamw I managed to solve this, by changing my mapping.
Instead of using a composite key, a table with its own unique ID was generated.
+----------------------+------------+
| RESPONSE_DOMAIN_CODE | TYPE |
+----------------------+------------+
| id | PK(BIGINT) |
| response_domain_id | BIGINT |
| code_id | BIGINT |
| rank | VARCHAR |
+----------------------+------------+
Now instead of using #Embeddable and #EmbeddedId I have a #ManyToOne and #OneToMany annotation on either side, and query based on ResponseDomain.
This enable full version audit control using Hibernate Envers also in relations like this.
I hope this will be helpful for someone at some point.

Exception querying inherited entities

In my db I have something similar:
+---------+
| answer |
+---------+ ----------------------->--+--------------+
| id +------) | answer_type_b|
---->-+--------------+ +--------------+
... | answer_type_a| | id |
+--------------+ | field_b |
| id | | ... |
| field_a |
| ... |
In my answer table I have common information, then I have two inheriting tables related to the parent table and having different specific fields.
I created model in my application using JPA 2 + Hibernate 4.2.6:
#Entity
public abstract class Answer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// ...
}
#Entity
public class AnswerA extends Answer{
#Column(name = "field_a")
private String fieldA;
// ...
}
#Entity
public class AnswerB extends Answer{
#Column(name = "field_b")
private String fieldB;
// ...
}
Now when I try to query all Answer entities I expect to get a collection with ALL the answers both Type A and Type B, instead I get an exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'answer0_.field_a' in 'field list'
I suppose this appens because there is no field_a field in parent table, so how can I solve this? Is there a way to query all the inheriting tables?
I solved following this interesting wiki-book.

JPA - Multiple columns discriminator

I have 3 tables like this in MySQL 5:
| PERSON |
| id | fullName | isEmp | isParent |
| EMPLOYEE |
| personId | code |
| PARENT |
| personId | job |
in which, Employee.personId and Parent.personId are foreign keys pointing to Person.id. An employee can also be a parent and vice versa. So how can I config using Annotation of JPA 2.0/Hibernate 3? Thanks!
If a Person can be both, you can't solve this through inheritance, because Java doesn't allow multiple inheritance. So you'll have to go with Aggregation, which is confusing on a semantic level, because it's has-a-parent instead of is-a-parent. But I'm afraid it's the way you'll have to go:
#Entity
public class Person{
#Id
private Long id;
#OneToOne(optional=true)
private Employee employee;
#OneToOne(optional=true)
private Parent parent;
public boolean isParent(){return parent!=null;}
public boolean isEmployee(){return employee!=null;}
}
#Entity
public class Employee{
#Id
private Long id;
#OneToOne(mappedBy="employee",optional=false)
private Person person;
}
#Entity
public class Parent{
#Id
private Long id;
#OneToOne(mappedBy="parent",optional=false)
private Person person;
}
(getters / setters etc. omitted)

Categories

Resources