Spring creating own columns with wrong reference in table - java

I have problem with "communication" between Spring and PostgreSQL.
My class User.java:
public static final String TABLE_NAME = "USERS";
public static final String SEQENCE = "USERS_SEQ";
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQENCE)
#SequenceGenerator(name = SEQENCE, sequenceName = SEQENCE, allocationSize = 1)
private Long userId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "department_id")
private Department department;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "organizazion_id")
private Organization organization;
Create table script for USERS:
CREATE TABLE IF NOT EXISTS USERS (
USER_ID BIGINT NOT NULL,
DEPARTMENT_ID BIGINT,
ORGANIZATION_ID BIGINT,
PRIMARY KEY (USER_ID),
FOREIGN KEY (DEPARTMENT_ID) REFERENCES DEPARTMENT(DEPARTMENT_ID),
FOREIGN KEY (ORGANIZATION_ID) REFERENCES ORGANIZATION(ORGANIZATION_ID)
);
Class Department.java
public static final String TABLE_NAME = "DEPARTMENT";
public static final String SEQENCE = "DEPARTMENT_SEQ";
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQENCE)
#SequenceGenerator(name = SEQENCE, sequenceName = SEQENCE, allocationSize = 1)
private Long departmentId;
private String departmentName;
.
.
. //Some other columns of primitive types about department..
Create table script for DEPARTMENT:
CREATE TABLE IF NOT EXISTS DEPARTMENT (
DEPARTMENT_ID BIGINT NOT NULL,
DEPARTMENT_NAME VARCHAR(50),
PRIMARY KEY (DEPARTMENT_ID)
);
Class Organization.java
public static final String TABLE_NAME = "ORGANIZATION";
public static final String SEQENCE = "ORGANIZATION_SEQ";
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQENCE)
#SequenceGenerator(name = SEQENCE, sequenceName = SEQENCE, allocationSize = 1)
private Long organizationId;
private String organizationName;
private String street;
private String postalCode;
private String state;
private String city;
Create table script for ORGANIZATION:
CREATE TABLE IF NOT EXISTS ORGANIZATION (
ORGANIZATION_ID BIGINT NOT NULL,
ORGANIZATION_NAME VARCHAR(50),
STREET VARCHAR(255),
POSTAL_CODE VARCHAR(50),
STATE VARCHAR(255),
CITY VARCHAR(255),
PRIMARY KEY (ORGANIZATION_ID)
);
When I run the program, the Spring create "own" columns in PostgreSQL, and it's look like:
USER_ID, DEPARTMENT_ID, ORGANIZATION_ID, DEPARTMENT_DEPARTMENT_ID, ORGANIZATION_ORGANIZATION_ID
How can I fix it ? It should be without columns
DEPARTMENT_DEPARTMENT_ID, ORGANIZATION_ORGANIZATION_ID
The annotation #Column(name = "column_name") can't be used here.

As join column you should reference the attribute in the other class:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQENCE)
#SequenceGenerator(name = SEQENCE, sequenceName = SEQENCE, allocationSize = 1)
private Long userId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "departmentId")
private Department department;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "organizazionId")
private Organization organization;

Related

How do i join between 2 entities with column other than primary key using annotations?

I have 2 tables Employee and Address. Each have primary key empId and addressId which is generated through sequence.
Employee Class:
public class Employee {
#Id
#Column(name = "sequence_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_seq")
#SequenceGenerator(sequenceName = "emp_seq", allocationSize = 1, name = "emp_seq")
private Long sequenceId;
#Column(name = "emp_id")
private String empId;
#Column(name = "joiningDate")
private Date businessDate;
#OneToOne(mappedBy = "employee", cascade = CascadeType.ALL)
private Address address;
}
Address Class
public class Address {
#Id
#Column(name = "address_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "address_seq")
#SequenceGenerator(sequenceName = "address_seq", allocationSize = 1, name = "address_seq")
private Long addressID;
#Column(name = "street")
private String street;
#OneToOne
#JoinColumn(name = "emp_id")
#JsonIgnore
private Employee employee;
}
I am using JPARepository to save both entities. here is my main class.
Employee e = Employee.builder().businessDate(new Date())
.empId("thinkerId").build();
Address a = Address.builder().street("32nd Street")
.employee(e).build();
e.setAddress(a);
empRepository.save(e);
Code runs successfully. But in my Address table, emp_Id column contains the primary key instead of the emp_id column.
How can i get "thinkerId" as value in Address table?
Thank you.
In Address entity you must explicitly specify the column you are referring to:
public class Address {
#Id
#Column(name = "address_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "address_seq")
#SequenceGenerator(sequenceName = "address_seq", allocationSize = 1, name = "address_seq")
private Long addressID;
#Column(name = "street")
private String street;
#OneToOne
#JoinColumn(name = "emp_id", referencedColumnName = "emp_id")
#JsonIgnore
private Employee employee;
}

Hibernate database relation annotations

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

How to create two entities and relate them

First of all, apologize for the grammatical errors that you can make. My English is not very good.
I'm trying to create dinamically an Entity and relathion with other Entity.
The idea is send a json file and get some properties to create that Entity, later associate that entity with the other. However, I can't because throw Exception like:
attempted to assign id from null one-to-one property
So, here in my SchemeService I try to create both entities:
protected Scheme createScheme(final String creatorId, final String name, final String description, final InputStream inputStream) {
DeserializeJSONFile desJsonFile = new DeserializeJSONFile();
desJsonFile.init(inputStream);
TableEntity table = new TableEntity();
table.setCreator(creatorId);
table.setProperties(desJsonFile.getProperties().toString());
table.setGeometry(desJsonFile.getGeometry().toString());
createTable(table);
Scheme scheme = new Scheme();
scheme.setCreator(creatorId);
scheme.setName(name);
scheme.setDescription(description);
scheme.setTable(table);
createScheme(scheme);
return scheme;
}
private void createTable(final TableEntity table) {
tableDao.create(table);
}
protected void createScheme(final Scheme scheme) {
schemeDao.create(scheme);
}
Here is my TableEntity:
public class TableEntity extends BaseEntityActivable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SCHEME_SEQ_GEN")
#SequenceGenerator(name = "SCHEME_SEQ_GEN", sequenceName = "test_seq_table", allocationSize = 1)
#Column(name = "table_id")
private Long tableId;
#Type(type= "jsonb")
#Column(name = "properties", columnDefinition = "json")
private String properties;
#Type(type= "jsonb")
#Column(name = "geometry", columnDefinition = "json")
private String geometry;
#OneToOne
#MapsId
private Scheme scheme;
}
Here is my SchemeEntity:
public class Scheme extends BaseEntityActivable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SCHEME_SEQ_GEN")
#SequenceGenerator(name = "SCHEME_SEQ_GEN", sequenceName = "test_seq_scheme", allocationSize = 1)
#Column(name = "scheme_id")
private Long schemeId;
#Column(name = "name", nullable = false)
#NotEmpty(message = AxisMapsErrorConstants.NAME_CANT_BE_EMPTY)
private String name;
#Column(name = "description")
private String description;
#OneToOne(mappedBy = "scheme", cascade = CascadeType.ALL)
#JoinColumn(name = "scheme_id", referencedColumnName = "table_id", foreignKey = #ForeignKey(name = "fk_scheme_table_1"))
private TableEntity table;
}
Here is my sql:
create sequence test_seq_table start 1 increment 1;
create sequence test_seq_scheme start 1 increment 1;
create table maps_table (
table_id int8 not null,
created_at timestamp not null,
created_by varchar(255),
updated_at timestamp,
updated_by varchar(255),
is_active boolean not null,
properties jsonb not null,
geometry jsonb not null,
primary key (table_id)
);
create table maps_scheme (
scheme_id int8 not null,
created_at timestamp not null,
created_by varchar(255),
updated_at timestamp,
updated_by varchar(255),
is_active boolean not null,
description varchar(255),
name varchar(255) not null,
table_id int8 not null,
primary key (scheme_id)
);
alter table maps_scheme
add constraint fk_scheme_table_1
foreign key (scheme_id)
references maps_table;
since you are using #mapsId this means you are using the same identifier in your relation with scheme, which means first the scheme should not be nullable and it should be available as managed entity each time your object is flushed, which also means that you can do the persistence only from one side of the relation since the id of scheme should be available when persisting your entity.
I am not sure if you really need #mapsId here, since you already have a bidirectional relation which means anyway you will be able to access both sides of your entity.
I would suggest to remove #mapsId here.
Thanks everyone for helping me.
This is my solution.
Scheme:
public class Scheme extends BaseEntityActivable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SCHEME_SEQ_GEN")
#SequenceGenerator(name = "SCHEME_SEQ_GEN", sequenceName = "aguas_seq_scheme", allocationSize = 1)
#Column(name = "scheme_id")
private Long schemeId;
#Column(name = "name", nullable = false)
#NotEmpty(message = AxisMapsErrorConstants.NAME_CANT_BE_EMPTY)
private String name;
#Column(name = "description")
private String description;
#OneToOne(cascade= { CascadeType.ALL }, fetch = FetchType.LAZY)
#JoinColumn(name="table_id")
private TableEntity table;
}
TableEntity:
public class TableEntity extends BaseEntityActivable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SCHEME_SEQ_GEN")
#SequenceGenerator(name = "SCHEME_SEQ_GEN", sequenceName = "aguas_seq_table", allocationSize = 1)
#Column(name = "table_id")
private Long tableId;
#Type(type= "jsonb")
#Column(name = "properties", columnDefinition = "json")
private String properties;
#Type(type= "jsonb")
#Column(name = "geometry", columnDefinition = "json")
private String geometry;
#OneToOne(mappedBy= "table")
private Scheme scheme;
}
SQL:
create sequence aguas_seq_table start 1 increment 1;
create sequence aguas_seq_scheme start 1 increment 1;
create table maps_table (
table_id int8 not null,
created_at timestamp not null,
created_by varchar(255),
updated_at timestamp,
updated_by varchar(255),
is_active boolean not null,
properties jsonb not null,
geometry jsonb not null,
primary key (table_id)
);
create table maps_scheme (
scheme_id int8 not null,
created_at timestamp not null,
created_by varchar(255),
updated_at timestamp,
updated_by varchar(255),
is_active boolean not null,
description varchar(255),
name varchar(255) not null,
table_id int8 not null,
primary key (scheme_id)
);
SchemeService to create scheme and table:
protected Scheme createScheme(final String creatorId, final String name, final String description, final InputStream inputStream) {
DeserializeJSONFile desJsonFile = new DeserializeJSONFile();
desJsonFile.init(inputStream);
TableEntity table = new TableEntity();
table.setCreator(creatorId);
table.setGeometry(desJsonFile.loadGeometries().toString());
table.setProperties(desJsonFile.loadProperties().toString());
createTable(table);
Scheme scheme = new Scheme();
scheme.setCreator(creatorId);
scheme.setName(name);
scheme.setDescription(description);
scheme.setTable(table);
createScheme(scheme);
return scheme;
}

JPA many-to-one with constant values in referenced table

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() {}
}

why is Hibernate selecting same columns twice?

Hibernate:
/* load entities.Department */ select
department0_.name as name4_0_,
department0_.id as id4_0_
from
J_DEPT department0_
where
department0_.name=?
Hibernate:
/* load one-to-many entities.Department.employees */ select
employees0_.dept as dept4_1_,
employees0_.id as id1_,
employees0_.id as id5_0_,
employees0_.dept as dept5_0_,
employees0_.name as name5_0_
from
J_EMP employees0_
where
employees0_.dept=?
Note that ID and DEPT columns are selected twice.
#Entity
#Table(name = "J_EMP")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
private String name;
#ManyToOne
#JoinColumn(name = "dept")
private Department deptNameInEmp;
}
#Entity
#Table(name = "J_DEPT")
public class Department {
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
#Id
#Column(name = "name")
private String deptNameInDeptEntity;
#OneToMany(mappedBy = "deptNameInEmp")
Set<Employee> employees;
}
UPDATED:
It was done intentionally to put #GeneratedValue on non-PK. However, now I've updated as you specified.
I've copy-pasted the new queries:
Hibernate:
/* load entities.Department */ select
department0_.id as id4_0_,
department0_.name as name4_0_
from
J_DEPT department0_
where
department0_.id=?
Hibernate:
/* load one-to-many entities.Department.employees */ select
employees0_.dept as dept4_1_,
employees0_.id as id1_,
employees0_.id as id5_0_,
employees0_.dept as dept5_0_,
employees0_.name as name5_0_
from
J_EMP employees0_
where
employees0_.dept=?
I guess its still the same.
And here are the tables:
CREATE TABLE "XYZ"."J_DEPT"
( "ID" NUMBER(*,0) NOT NULL ENABLE,
"NAME" VARCHAR2(255 BYTE) NOT NULL ENABLE,
CONSTRAINT "J_DEPT_PK" PRIMARY KEY ("ID")
)
CREATE TABLE "XYZ"."J_EMP"
( "ID" NUMBER NOT NULL ENABLE,
"NAME" VARCHAR2(255 BYTE),
"DEPT" NUMBER NOT NULL ENABLE,
CONSTRAINT "J_EMP_PK" PRIMARY KEY ("ID"))
here is the code -i'm pasting here as it is :
#Entity
#Table(name = "J_DEPT")
public class Department {
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
#Id
private Long id;
#Column(name = "name")
private String deptNameInDeptEntity;
#OneToMany(mappedBy = "deptNameInEmp")
Set<Employee> employees;
public Set<Employee> getEmployees() {
return employees;
}
}
#Entity
#Table(name = "J_EMP")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
private String name;
#ManyToOne
#JoinColumn(name = "dept")
private Department deptNameInEmp;
public Employee() {
}
And here is the test case:
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
#ContextConfiguration(locations = { "classpath:test-applicationContext.xml" })
#Transactional
public class EmpTest {
#Autowired
private SessionFactory sessionFactory;
#Test
public void testDept() {
final Department find = (Department) sessionFactory.getCurrentSession()
.get(Department.class, Long.parseLong("1"));
System.out.println("find res = " + find);
}
}
Probably because of this part:
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
#Id
#Column(name = "name")
private String deptNameInDeptEntity;
There is something wrong here, the GeneratedValue cannot be applied to a non PK. Did you mean:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq")
#SequenceGenerator(name = "mySeq", sequenceName = "CNTRY_SEQ")
private Long id;
#Column(name = "name")
private String deptNameInDeptEntity;
If not, and if this was intentional, please explain your goal and show your table(s).
Update: I can't reproduce the problem. I copy pasted the code you provided and here is the query I get:
select
employee37x0_.id as id135_,
employee37x0_.dept as dept135_,
employee37x0_.name as name135_
from
J_EMP employee37x0_
where
employee37x0_.id=?
Works as expected.
A Google search on "hibernate duplicate aliases" reveals some (old) issues so I don't exclude anything but I couldn't find any proof of recent existing problems. Can you provide a test case (using an embedded database)?

Categories