I have 2 tables employee and employee_document. Here is the mysql query for two tables -
CREATE TABLE employee (
id int(11) unsigned NOT NULL,
name varchar(100) COLLATE utf8_unicode_ci NOT NULL,
email varchar(50) COLLATE utf8_unicode_ci NOT NULL,
password_hash varchar(100) COLLATE utf8_unicode_ci NOT NULL,
status int(11) NOT NULL,
creation_date bigint(20) NOT NULL,
created_by int(11) DEFAULT NULL,
update_date bigint(20) NOT NULL,
updated_by int(11) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE employee_document (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
employee_id int(11) unsigned NOT NULL,
file_id int(11) unsigned NOT NULL,
document_type varchar(50) COLLATE utf8_unicode_ci DEFAULT '',
status int(11) DEFAULT NULL,
creation_date bigint(20) DEFAULT NULL,
created_by int(11) unsigned NOT NULL,
update_date bigint(20) DEFAULT NULL,
updated_by int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
In my spring boot project I used hibernate with JPA Data. Here are the java interpretation of these tables.
#MappedSuperclass
public abstract class AbstractTimestampEntity {
#Transient
Logger log = LoggerFactory.getLogger(AbstractTimestampEntity.class);
#Column(name = "creation_date", nullable = false, updatable=false)
private Long creationDate;
#Column(name = "update_date", nullable = false)
private Long updateDate;
#PrePersist
protected void onCreate() {
log.debug("onCreate");
updateDate = creationDate = System.currentTimeMillis();
}
#PreUpdate
protected void onUpdate() {
log.debug("onUpdate");
updateDate = System.currentTimeMillis();
}
}
#Entity
#Table(name = "employee")
public class Employee extends AbstractTimestampEntity implements Serializable {
private static final long serialVersionUID = 1L;
public static final Integer STATUS_INACTIVE = 0;
public static final Integer STATUS_ACTIVE = 1;
public static final Integer STATUS_ARCHIVED = -1;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Integer id;
#Column(name = "name", nullable = false)
public String name;
#Email
#Column(name = "email", nullable = false, unique = true)
public String email;
#JsonIgnore
#Column(name = "password_hash", nullable = false)
public String passwordHash;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "employee", fetch = FetchType.EAGER)
private Set<EmployeeDocument> documents;
#Max(1)
#Min(-1)
#Column(name = "status", nullable = false )
public Integer status;
#Column(name = "created_by", updatable = false)
public Integer createdById;
#Column(name = "updated_by")
public Integer updatedById;
}
#Entity
#Table(name = "employee_document")
public class EmployeeDocument extends AbstractTimestampEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Transient
public static final Integer STATUS_INACTIVE = 0;
#Transient
public static final Integer STATUS_ACTIVE = 1;
#Transient
public static final Integer STATUS_ARCHIVED = -1;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Integer id;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "employee_id", nullable = false)
public Employee employee;
#OneToOne
#JoinColumn(name = "file_id")
public EmployeeFile employeeFile;
#Max(1)
#Min(-1)
#Column(name = "status", nullable = false )
public Integer status;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "created_by", updatable = false)
#JsonBackReference
public Employee createdBy;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "updated_by")
#JsonBackReference
public Employee updatedBy;
}
This code does not execute, the app fails to start and throws DuplicateMappingException. Here's the full exception stack -
org.hibernate.DuplicateMappingException: Table [employee_document] contains physical column name [created_by] referred to by multiple physical column names: [createdBy], [created_by]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl$TableColumnNameBinding.bindPhysicalToLogical(InFlightMetadataCollectorImpl.java:922)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl$TableColumnNameBinding.addBinding(InFlightMetadataCollectorImpl.java:891)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.addColumnNameBinding(InFlightMetadataCollectorImpl.java:961)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.addColumnNameBinding(InFlightMetadataCollectorImpl.java:942)
at org.hibernate.cfg.Ejb3Column.addColumnBinding(Ejb3Column.java:407)
at org.hibernate.cfg.Ejb3Column.linkWithValue(Ejb3Column.java:369)
at org.hibernate.cfg.annotations.SimpleValueBinder.linkWithValue(SimpleValueBinder.java:431)
at org.hibernate.cfg.annotations.SimpleValueBinder.make(SimpleValueBinder.java:407)
at org.hibernate.cfg.annotations.PropertyBinder.makePropertyAndValue(PropertyBinder.java:187)
at org.hibernate.cfg.annotations.PropertyBinder.makePropertyValueAndBind(PropertyBinder.java:199)
at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:2225)
at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:911)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:738)
at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:245)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:222)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:265)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:847)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:874)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:353)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:373)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:362)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 19 common frames omitted
What am I doing wrong? Thanks in advance.
You are trying to reference the Empolyee three times from the EmployeeDocument but only one of those is by primary key. The other two are referenced by non-primary key columns and you would need to use the referencedColumnName option additionally to make this work:
#JsonIgnore
#ManyToOne
#JoinColumn(name = "employee_id", nullable = false)
public Employee employee;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "created_by", referencedColumnName="created_by", updatable = false)
#JsonBackReference
public Employee createdBy;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "updated_by". , referencedColumnName="updated_by")
#JsonBackReference
public Employee updatedBy;
Related
I have a spring boot application with two entities in a relationship. MeetingSetting and MeetingTime meetingSetting can have unlimited meetingTimes. So far the databases are generating without problem and I can halfway save the values as well. But there is one problem. meetingName is a string and used as a foreign key in meetingTime but when the database are generated for some reason it is added as a bigint and I could not find the reason for that, because everywhere it is used as string. Could someone look at my code and tell me my mistake?
MeetingSettings:
#Entity
#Table(name = "meeting_settings")
#Data
public class MeetingsSetting {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_name", unique = true)
private String meetingName;
#Column(name = "meeting_url")
private String meetingUrl;
#Column(name = "meeting_pw")
private String meetingPw;
#OneToMany(mappedBy = "meeting_Name", cascade = CascadeType.ALL)
private Set<MeetingTime> meetingTime = new HashSet<>();
}
MeetingTime:
#Entity
#Table(name = "meeting_times")
#Data
public class MeetingTime {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_date")
private String date;
#Column(name = "start_time")
private String startTime;
#Column(name = "end_time")
private String endTime;
#ManyToOne
#JoinColumn(name = "meeting_name" ,insertable = false, updatable = false)
private MeetingsSetting meeting_Name;
}
This is my application property:
spring.datasource.url=jdbc:mysql://localhost:3306/coorporate_blinddate?createDatabaseIfNotExist=true&useSSL=true&serverTimezone=UTC
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=../generate.sql
spring.jpa.show-sql= true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto = update
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=Test1234##1
server.port=8081
and the script used for db generation:
-- auto-generated definition
create table meeting_settings
(
id bigint auto_increment
primary key,
meeting_name varchar(255) null,
meeting_pw varchar(255) null,
meeting_url varchar(255) null
);
-- auto-generated definition
create table meeting_times
(
id bigint auto_increment
primary key,
meeting_date varchar(255) null,
start_time varchar(255) null,
end_time varchar(255) null,
meeting_name varchar(255) null,
constraint fk_meeting_times_meeting_name
foreign key (meeting_name) references meeting_settings (meeting_name)
);
I fixed this with the big int by adding referencedColumnName = "meeting_name" to this in meetingTime:
#ManyToOne
#JoinColumn(name = "meeting_name" ,insertable = false, updatable = false)
private MeetingsSetting meeting_Name;
changed to:
#ManyToOne
#JoinColumn(name = "meeting_name" ,insertable = false, updatable = false, referencedColumnName = "meeting_name")
private MeetingsSetting meeting_Name;
I've been struggling with this for so long now. I have a database with two tables "product" and "categories"
CREATE TABLE `product` (
`idproduct` int NOT NULL AUTO_INCREMENT,
`idcategory` int DEFAULT NULL,
`product_name` varchar(255) DEFAULT NULL,
`product_category` varchar(255) DEFAULT NULL,
`product_description` varchar(255) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idproduct`),
KEY `fkcat` (`idcategory`),
CONSTRAINT `fkcat` FOREIGN KEY (`idcategory`) REFERENCES `categories` (`idcategory`)
) ENGINE=InnoDB AUTO_INCREMENT=149 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `categories` (
`idcategory` int NOT NULL AUTO_INCREMENT,
`category_name` varchar(255) NOT NULL,
`category_description` varchar(255) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idcategory`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Now I'm trying to get a hibernate join query so I can retrieve let's say
product_name and category_name
One product belongs only to one category (for example, if the product is "black t-shirt", its value for the column "idcategory" would be 2. This is enforced by the foreign key.
The table categories entries can be associated with more than one product (for example, "category_name" = 2 can be associated with many products.
How can this design be implemented in hibernate entities? I've tried this but isn't working...
#Entity
#Table(name = "product")
public class Product implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idproduct")
private int idproduct;
#Column(name = "idcategory")
private int idcategory;
#Column(name = "product_name")
private String productName;
#Column(name = "product_description")
private String productdescription;
#Column(name = "product_category")
private String productcategory;
#OneToMany(targetEntity = Categories.class, cascade = CascadeType.ALL)
#JoinColumn(name = "idcategory",referencedColumnName="idcategory")
private List<Categories> category;
#Entity
#Table(name = "categories")
public class Categories {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idcategory")
private int idcategory;
#Column(name = "category_name")
private String category_name;
#Column(name = "category_description")
private String category_description;
and the query is
SELECT p, c FROM Product p INNER JOIN p.category c
this is not correct
#OneToMany(targetEntity = Categories.class, cascade = CascadeType.ALL)
#JoinColumn(name = "idcategory",referencedColumnName="idcategory")
private List<Categories> category;
Product can't have many categories... it is actually the reverse ->
#Entity
#Table(name = "categories")
public class Categories {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idcategory")
private int idcategory;
#Column(name = "category_name")
private String category_name;
#Column(name = "category_description")
private String category_description;
#OneToMany(cascade = CascadeType.ALL, mappedBy="category")
private List<Product> products;
and Product
#Entity
#Table(name = "product")
public class Product implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idproduct")
private int idproduct;
#Column(name = "idcategory")
private int idcategory;
#Column(name = "product_name")
private String productName;
#Column(name = "product_description")
private String productdescription;
#Column(name = "product_category")
private String productcategory;
#ManyToOne
private Categories categories;
Suggestion : rename Categories to Category
I have a many to many relationship between user and group with addtional columns in the join table. It looks like this:
When I delete an user, it should remove all his references from user_to_group(which works) and all the groups created by him should remain and have their created_by field updated to NULL(this doesn't happen, all the entries are deleted).
DDL for schema:
CREATE TABLE user (
user_id int NOT NULL AUTO_INCREMENT,
first_name varchar(100) NOT NULL,
last_name varchar(100) NOT NULL,
username varchar(100) NOT NULL,
email_address varchar(100) UNIQUE NOT NULL,
phone_number varchar(100) NOT NULL,
password varchar(255) NOT NULL,
notification_type varchar(30) NOT NULL DEFAULT "email",
date_created datetime NOT NULL,
is_active bool NOT NULL DEFAULT false,
CONSTRAINT user_pk PRIMARY KEY (user_id)
);
CREATE TABLE `group` (
group_id int NOT NULL AUTO_INCREMENT,
name varchar(100) NULL,
date_created datetime NOT NULL,
is_private bool NOT NULL DEFAULT false,
created_by int NULL,
CONSTRAINT group_pk PRIMARY KEY (group_id),
CONSTRAINT group_user_fk FOREIGN KEY(created_by)
REFERENCES user (user_id) ON DELETE SET NULL
);
CREATE TABLE user_to_group (
user_id int NOT NULL,
group_id int NOT NULL,
user_type_id int NOT NULL,
is_blocked bool NOT NULL DEFAULT false,
CONSTRAINT user_to_group_pk PRIMARY KEY (user_id,group_id),
CONSTRAINT user_to_group_group_fk FOREIGN KEY(group_id)
REFERENCES `group` (group_id),
CONSTRAINT user_to_group_user_type_fk FOREIGN KEY(user_type_id)
REFERENCES user_type (id),
CONSTRAINT user_to_group_user_fk FOREIGN KEY(user_id)
REFERENCES user (user_id)
);
User Entity:
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "username",
unique = true)
private String username;
#Column(name = "email_address",
unique = true)
private String emailAddress;
#Column(name = "phone_number")
private String phoneNumber;
#Column(name = "password")
private String password;
#Column(name = "notification_type",
insertable = false)
private String notificationType = "email";
#Column(name = "date_created")
private Date dateCreated;
#Column(name = "is_active",
insertable = false)
private Boolean active = false;
#OneToMany(
mappedBy = "user",
cascade = {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST},
orphanRemoval = true
)
private List<UserGroup> groups;
}
Group Entity:
#Entity
#Table(name = "`group`")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "date_created")
private Date dateCreated;
#Column(name = "is_private",
insertable = false)
private Boolean privateG = false;
#OneToOne(fetch = FetchType.LAZY,
cascade = {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST},
orphanRemoval = true)
#JoinColumn(name = "created_by")
private User createdBy;
#OneToMany(
mappedBy = "group",
cascade = {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST},
orphanRemoval = true
)
private List<UserGroup> users = new ArrayList<>();
}
UserGroup(join table):
#Entity
#Table(name = "user_to_group")
public class UserGroup {
#EmbeddedId
private UserGroupId id;
#ManyToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#MapsId("userId")
#JoinColumn(name = "user_id", insertable = false, updatable = false)
private User user;
#ManyToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#MapsId("groupId")
#JoinColumn(name = "group_id", insertable = false, updatable = false)
private Group group;
#Column(name = "is_blocked",
insertable = false)
private boolean isBlocked = false;
}
Ignore user_type_id field on the join table. If I delete an user with on the workbench, it works as expected(created_by field updates to NULL). But if i use this:
#Override
#Transactional
public User deleteUser(Long id) {
Optional<User> userToDelete = userRepository.findById(id);
userToDelete.ifPresent(user -> userRepository.delete(user));
return userToDelete.orElseThrow(() -> new UserNotFoundException("User not found"));
}
the entire row in the group table is deleted. What am I doing wrong?
#ManyToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#MapsId("groupId")
#JoinColumn(name = "group_id", insertable = false, updatable = false)
private Group group;
exclude CascadeType.REMOVE and group will be intact.
Can anyone help me with this and tell me what I'm missing. Have gone through a number of examples and seem to have everything configured correctly but I keep getting this exception:
org.hibernate.AnnotationException: A Foreign key refering com.bank.entity.Customer from com.bank.entity.Account has the wrong number of column. should be 2
I have a class called Branch that has 1:M relationship with Customer. Customer in turn has a 1:M relationship with Account.
Note: Customer also has an embeddable Address class
Here is my code:
Branch Class
#Entity
#Table(name = "Branch")
public class Branch extends AbstractPersistable<Long> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "branch_Name")
private String branchName;
#OneToMany(mappedBy = "branch")
private Set<Customer> customers;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Embeddable Address Class
#Embeddable
public class Address {
#Column(name = "houseNumber", nullable = false)
private String houseNumber;
#Column(name = "streetName", nullable = false)
private String streetName;
#Column(name = "city", nullable = false)
private String city;
#Column(name = "country", nullable = false)
private String country;
#Column(name = "eirCode", nullable = false)
private String eirCode;
}
Customer Class
#Entity
#Table(name = "Customer")
public class Customer extends AbstractPersistable<Long> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "first_Name")
private String firstName;
#Column(name = "surname")
private String surName;
#Embedded
Address address;
#ManyToOne
#JoinColumn(name = "branchId", nullable = false)
private Branch branch;
#OneToMany(mappedBy = "customer")
private Set<Account> accounts;
}
Account Class
#Entity
#Table(name = "Account")
public class Account extends AbstractPersistable<Long> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "account_type")
private String type;
#Column(name = "interest_rate")
private double rate;
#Column(name = "account_balance")
private double balance;
#ManyToOne
#JoinColumn(name = "customerId", nullable = false)
private Customer customer;
}
Here I create the tables
CREATE TABLE IF NOT EXISTS `Branch` (
`id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`branch_Name` VARCHAR(25) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `Customer` (
`id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`first_Name` VARCHAR(25) NOT NULL,
`surname` VARCHAR(25) NOT NULL,
`houseNumber` VARCHAR(25) NOT NULL,
`streetName` VARCHAR(120) NOT NULL,
`city` VARCHAR(25) NOT NULL,
`country` VARCHAR(25) NOT NULL,
`eirCode` VARCHAR(25) NOT NULL,
`branchId` BIGINT(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK_CUST_BRANCH` (`branchId`),
CONSTRAINT `FK_CUST_BRANCH` FOREIGN KEY (`branchId`) REFERENCES `Branch` (`id`)
);
CREATE TABLE IF NOT EXISTS `Account` (
`id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`account_type` VARCHAR(25) NOT NULL,
`interest_rate` DOUBLE NOT NULL,
`account_balance` DOUBLE NOT NULL,
`customerId` BIGINT(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK_CUST_ACC` (`customerId`),
CONSTRAINT `FK_CUST_ACC` FOREIGN KEY (`customerId`) REFERENCES `Customer` (`id`)
);
In Account you are saying :
#ManyToOne
#JoinColumn(name = "customerId", nullable = false)
private Customer customer;
But there is not column with name customerId(?) so you should give name to primary key of Customer
try changing this in Customer
#Entity
#Table(name = "Customer")
public class Customer extends AbstractPersistable<Long> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="customerId")
private Long id;
...
}
I'm doing Spring Boot project and use spring-boot-jpa (Hibernate implementation). I'm having trouble configuring following relation between entities.
Let's assume I need many-to-one (and reversly one-to-many) relation between two tables (MySQL in this example, table1 logically stores description for codes in various other tables) :
CREATE TABLE `table1` (
`id` INT NOT NULL AUTO_INCREMENT,
`ref_table` VARCHAR(50) NOT NULL,
`ref_column` VARCHAR(50) NOT NULL,
`code` VARCHAR(10) NOT NULL,
`description` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `u_composite1` (`ref_table` ASC, `ref_column` ASC, `code` ASC));
CREATE TABLE `table2` (
`id` INT NOT NULL AUTO_INCREMENT,
`field1` VARCHAR(100) NULL,
`code` VARCHAR(10) NOT NULL,
PRIMARY KEY (`id`));
The way I join these two tables in SQL is like this:
SELECT t2.*, t1.description
FROM table2 t2
JOIN table1 t1
ON ( t1.ref_table = 'table2'
AND t1.ref_column = 'code'
AND t1.code = t2.code);
So, I created entities like this (minus the getters an setters):
#Entity
#Table(name = "table1")
public class Table1 implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private int id;
#Column(nullable = false, length = 10)
private String code;
#Column(length = 100)
private String description;
#Column(name = "ref_column", nullable = false, length = 50)
private String refColumn;
#Column(name = "ref_table", nullable = false, length = 50)
private String refTable;
#OneToMany(mappedBy = "table1")
private List<Table2> table2;
}
#Entity
#Table(name = "table2")
public class Table2 implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private int id;
#Column(nullable = false, length = 45)
private String field1;
#ManyToOne(fetch=FetchType.LAZY)
#Column(name = "code")
#JoinColumns({
#JoinColumn(name = "code", referencedColumnName = "code", nullable = false, updatable = false),
#JoinColumn(name = "'table2'", referencedColumnName = "ref_table", nullable = false, updatable = false),
#JoinColumn(name = "'code'", referencedColumnName = "ref_column", nullable = false, updatable = false)
})
private Table1 table1;
}
But it doesn't work. :(
Can this kind of relation even be defined in JPA?
If so, please, how?
Pertaining to the "join with constant values" problem I managed to make it work using the #Where Hibernate annotation:
How to replace a #JoinColumn with a hardcoded value?
#Entity
#Table(name = "a")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long id;
#OneToMany
#JoinColumn(name = "id", referencedColumnName = "id")
#Where(clause = "blah = 'CONSTANT_VALUE'")
public Set<B> b;
protected A() {}
}
#Entity
#Table(name = "b")
public class B {
#Id
#Column(nullable = false)
public Long id;
#Column(nullable = false)
public String blah;
protected B() {}
}