Hibernate MapKeyManyToMany gives composite key where none exists - oracle

I have a Hibernate (3.3.1) mapping of a map using a three-way join table:
#Entity
public class SiteConfiguration extends ConfigurationSet {
#ManyToMany
#MapKeyManyToMany(joinColumns=#JoinColumn(name="SiteTypeInstallationId"))
#JoinTable(
name="SiteConfig_InstConfig",
joinColumns = #JoinColumn(name="SiteConfigId"),
inverseJoinColumns = #JoinColumn(name="InstallationConfigId")
)
Map<SiteTypeInstallation, InstallationConfiguration>
installationConfigurations = new HashMap<SiteTypeInstallation, InstallationConfiguration>();
...
}
The underlying table (in Oracle 11g) is:
Name Null Type
------------------------------ -------- ----------
SITECONFIGID NOT NULL NUMBER(19)
SITETYPEINSTALLATIONID NOT NULL NUMBER(19)
INSTALLATIONCONFIGID NOT NULL NUMBER(19)
The key entity used to have a three-column primary key in the database, but is now redefined as:
#Entity
public class SiteTypeInstallation implements IdResolvable {
#Id
#GeneratedValue(generator="SiteTypeInstallationSeq", strategy= GenerationType.SEQUENCE)
#SequenceGenerator(name = "SiteTypeInstallationSeq", sequenceName = "SEQ_SiteTypeInstallation", allocationSize = 1)
long id;
#ManyToOne
#JoinColumn(name="SiteTypeId")
SiteType siteType;
#ManyToOne
#JoinColumn(name="InstalationRoleId")
InstallationRole role;
#ManyToOne
#JoinColumn(name="InstallationTypeId")
InstType type;
...
}
The table for this has a primary key 'Id' and foreign key constraints+indexes for each of the other columns:
Name Null Type
------------------------------ -------- ----------
SITETYPEID NOT NULL NUMBER(19)
INSTALLATIONROLEID NOT NULL NUMBER(19)
INSTALLATIONTYPEID NOT NULL NUMBER(19)
ID NOT NULL NUMBER(19)
For some reason, Hibernate thinks the key of the map is composite, even though it isn't, and gives me this error:
org.hibernate.MappingException: Foreign key (FK1A241BE195C69C8:SiteConfig_InstConfig [SiteTypeInstallationId])) must have same number of columns as the referenced primary key (SiteTypeInstallation [SiteTypeId,InstallationRoleId])
If I remove the annotations on installationConfigurations and make it transient, the error disappears.
I am very confused why it thinks SiteTypeInstallation has a composite key at all when #Id is clearly defining a simple key, and doubly confused why it picks exactly just those two columns. Any idea why this happens? Is it possible that JBoss (5.0 EAP) + Hibernate somehow remembers a mistaken idea of the primary key across server restarts and code redeployments?
Thanks in advance,
-Lars

Related

Get list of database errors on saveAll()

I have a CountryObject:
#Entity
#Table(name = "country")
class CountryObject(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
val id: Long? = null,
#Column(name = "name")
val name: String,
#Column(name = "ISO2", length = 2)
val ISO2: String,
#Column(name = "ISO3", length = 3)
val ISO3: String
) : Serializable
with the corresponding PostgreSQL create script that has some unique constraints on ISO2 and ISO3:
CREATE TABLE country
(
id BIGINT NOT NULL,
name VARCHAR(255) NOT NULL,
iso2 VARCHAR(2) NOT NULL,
iso3 VARCHAR(3) NOT NULL,
CONSTRAINT pk_country PRIMARY KEY (id)
);
ALTER TABLE country
ADD CONSTRAINT UNIQUE_CONSTRAINT_ISO2 UNIQUE (iso2);
ALTER TABLE country
ADD CONSTRAINT UNIQUE_CONSTRAINT_ISO3 UNIQUE (iso3);
Along with this I have a corresponding repository:
#Repository
interface CountryRepository : JpaRepository<CountryObject, Long>
And out of the box implementation of the CountryRepository has a saveAll() method that tries to insert a list of objects into the country table. So far, everything behaves as expected.
Once I try to insert a list that have multiple objects violating the unique constraints, the exception is thrown on the first and stopped. What I want to have is to get some list with the errors that I can use as a report, which mentions which entries failed saving. Of course nothing should be saved in that case and the transaction is rolled back.
Any hints how something like this can me achieved? Of course one option would be to try to save each object separately and then collect the errors, but that might be not that performant.

Strange validation conflict in Spring JPA TableGenerator

I have a legacy database with composite primary key in table project. (BaseEntity contains common properties for lastModifiedDate and lastModifiedBy)
#Entity
#IdClass(ProjectPk.class)
public class Project extends BaseEntity {
#Id
#GeneratedValue(strategy=GenerationType.TABLE, generator="nextProjectId")
#TableGenerator(
name="nextProjectId",
table="projectId",
pkColumnName = "proj_Id",
pkColumnValue="proj_id"
)
private Long projId;
#Id
private int version;
//other properties, getters and setters omitted for clarity
}
PK class
public class ProjectPk implements java.io.Serializable {
private int projId;
private int version;
//both constructoirs, equals, hashcode, getters and setters omitted for clarity
}
I have flyway migration files to simulate production database.
drop table if exists project;
CREATE TABLE project
(
proj_id bigint,
version int,
-- other columns omitted for clarity
PRIMARY KEY (`proj_id`, `version`)
) ENGINE=InnoDB;
drop table if exists project_id;
CREATE TABLE project_id
(
proj_id bigint
) ENGINE=InnoDB;
flyway creates tables as ordered in migration file
Table: project_id
Columns:
proj_id bigint
...
Table: project
Columns:
proj_id bigint PK
version int PK
...
during maven build I'm getting validation error
Schema-validation: wrong column type encountered in column [proj_id] in table [project_id]; found [bigint (Types#BIGINT)], but expecting [varchar(255) (Types#VARCHAR)]
What I did wrong to make hibernate expect [varchar(255) (Types#VARCHAR)]?
This is SpringBoot project 2.6.6 with MySql database
I see the following problems with your code:
Type mismatch between Project.projId (Long type) and ProjectPk.projId (int type).
You use wrong table structure for the project_id table.
You can see a working example below.
Assuming that you have the following tables:
CREATE TABLE test_project
(
proj_id bigint,
version int,
title VARCHAR(50),
PRIMARY KEY (proj_id, version)
);
create table table_identifier (
table_name varchar(255) not null,
product_id bigint,
primary key (table_name)
);
insert into table_identifier values ('test_project', 20);
and the following mapping:
#Entity
#Table(name = "test_project")
#IdClass(ProjectPk.class)
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "nextProjectId")
#TableGenerator(
name="nextProjectId",
table="table_identifier",
pkColumnName = "table_name",
valueColumnName="product_id",
allocationSize = 5
)
#Column(name = "proj_id")
private Long projId;
#Id
private int version;
// other fields, getters, setters ...
}
you will be able to persist the entity like below:
Project project = new Project();
project.setVersion(1);
// ...
entityManager.persist(project);

Cannot get many to many relationship in spring bootstrap

I have a very simple many to many scenario: One ORDER has many PRODUCT, and each product can belong to many orders.
order :
#Entity
#Table(name = "ORDER")
public class OrderEntity {
#Id
#Column(name="ORDER_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="ORDER_NAME")
private String name;
#Column(name="ORDER_DATE")
private Date date;
#ManyToMany
private List<ProductEntity> selectedProducts = new ArrayList<>();
product:
#Entity
#Table(name = "PRODUCT")
public class ProductEntity {
#Id
#Column(name="PRODUCT_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="PRODUCT_NAME")
private String name;
#Column(name="PRODUCT_PRICE")
private BigDecimal price;
#ManyToMany
private List<OrderEntity> orders = new ArrayList<>();
(removed getters and setters and constructors for brevity)
However when I startup bootstrap then I get a whole host of errors :
Error executing DDL "drop table order if exists" via JDBC Statement
Syntax error in SQL statement "DROP TABLE ORDER[*] IF EXISTS "; expected "identifier"; SQL statement:
Error executing DDL "create table order (order_id bigint not null, order_date timestamp, order_name varchar(255), primary key (order_id))" via JDBC Statement
Syntax error in SQL statement "CREATE TABLE ORDER[*] (ORDER_ID BIGINT NOT NULL, ORDER_DATE TIMESTAMP, ORDER_NAME VARCHAR(255), PRIMARY KEY (ORDER_ID)) "; expected "identifier"; SQL statement:
create table order (order_id bigint not null, order_date timestamp, order_name varchar(255), primary key (order_id)) [42001-199]
Error executing DDL "alter table order_selected_products add constraint FKrbll8c9ubhjqangdfw2sgkurw foreign key (order_entity_order_id) references order" via JDBC Statement
Syntax error in SQL statement "ALTER TABLE ORDER_SELECTED_PRODUCTS ADD CONSTRAINT FKRBLL8C9UBHJQANGDFW2SGKURW FOREIGN KEY (ORDER_ENTITY_ORDER_ID) REFERENCES ORDER[*] "; expected "identifier"; SQL statement:
alter table order_selected_products add constraint FKrbll8c9ubhjqangdfw2sgkurw foreign key (order_entity_order_id) references order [42001-199]
Error executing DDL "alter table product_orders add constraint FK9pa3r9u6x44jjxrkkhdvhu23k foreign key (orders_order_id) references order" via JDBC Statement
Syntax error in SQL statement "ALTER TABLE PRODUCT_ORDERS ADD CONSTRAINT FK9PA3R9U6X44JJXRKKHDVHU23K FOREIGN KEY (ORDERS_ORDER_ID) REFERENCES ORDER[*] "; expected "identifier"; SQL statement:
alter table product_orders add constraint FK9pa3r9u6x44jjxrkkhdvhu23k foreign key (orders_order_id) references order [42001-199]
I'm not sure why there are these syntax errors. Is this some kind of SQL dialect issue?
ORDER is a very common reserved keyword and that is the root cause of the errors you see.
Change your table name to something else, such as ORDERS, or if you really want to use that name you can try escaping it:
#Entity
#Table(name = "\"ORDERS\"")
public class OrderEntity {
....
}
List of reserved keywords for some common databases:
https://docs.oracle.com/cd/B28359_01/appdev.111/b31231/appb.htm#BABDFFBA
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/reserved-keywords-transact-sql?view=sql-server-2017
https://www.postgresql.org/docs/current/sql-keywords-appendix.html
https://dev.mysql.com/doc/refman/8.0/en/keywords.html

Spring Boot + PostgreSql Sequence not available

I am working in a project developed with Spring Boot and PostgreSql as Database.
I have created a sequence user_seq in PostgreSql for my user table
CREATE SEQUENCE "USER_MGMT"."USER_SEQ"
INCREMENT 1
START 1000
MINVALUE 1000
MAXVALUE 99999999
CACHE 1;
ALTER SEQUENCE "USER_MGMT"."USER_SEQ"
OWNER TO postgres;
Here is my user Table
CREATE TABLE "USER_MGMT"."USER"
(
"USER_ID" bigint NOT NULL DEFAULT nextval('"USER_MGMT"."USER_SEQ"'::regclass),
"FIRST_NAME" character varying(30) COLLATE pg_catalog."default" NOT NULL,
"LAST_NAME" character varying(50) COLLATE pg_catalog."default" NOT NULL,
"EMAIL_ID" character varying(70) COLLATE pg_catalog."default" NOT NULL,
"DESK_NUMBER" bigint,
"MOBILE_NUMBER" bigint,
"IS_ACTIVE" boolean NOT NULL DEFAULT true,
"CREATED_BY" character varying(70) COLLATE pg_catalog."default",
"MODIFIED_BY" character varying(70) COLLATE pg_catalog."default",
"DATE_CREATED" timestamp without time zone,
"DATE_MODIFIED" timestamp without time zone,
CONSTRAINT "USER_ID_PK" PRIMARY KEY ("USER_ID"),
CONSTRAINT "EMAIL_ID_UK" UNIQUE ("EMAIL_ID"),
CONSTRAINT "MOBILE_NUMBER_UK" UNIQUE ("MOBILE_NUMBER")
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE "USER_MGMT"."USER"
OWNER to postgres;
I am creating an JPA entity for this user table,
#Entity
#Table(name = "`USER`")
#ToString
#EqualsAndHashCode
public class User extends Auditable<String> {
/**
*
*/
private static final long serialVersionUID = 5351767213835401599L;
#Id
#SequenceGenerator(name = "USER_ID", sequenceName = "USER_SEQ", allocationSize = 1)
//Tried this also but not works
#SequenceGenerator(name = "USER_ID", sequenceName = "`USER_SEQ`", allocationSize = 1)
#GeneratedValue(generator = "USER_ID", strategy = GenerationType.SEQUENCE)
#Column(name = "`USER_ID`")
private Long userId;
#Column(name = "`FIRST_NAME`")
private String firstName;
While inserting data to this user table, I am getting error says user_seq is not available.
ERROR: relation "USER_MGMT.user_seq" does not exist
It tooks as lower case even though I mentioned in Upper case in entity.This is must for upper case for all the tables and sequences. Is there anything I missed in entity?

JPA not returning the records which contain empty columns in the EmbeddedId

JpaRepository's findAll() method does not return the rows, if any of the field in the composite key is null.
This is the entity class with the EmbeddedId JobVaccinationPK
/**
* ApplicationParam entity. #author MyEclipse Persistence Tools
*/
#Entity
#Table(name="job_vaccination",schema="cdcis")
#SuppressWarnings("serial")
public class JobVaccination implements java.io.Serializable {
// Fields
#Column(name="default_yn", length=1)
private String defaultYn;
#EmbeddedId
private JobVaccinationPK jobVaccinationPK;
public JobVaccination(){
}
//setters getters
}
This is the Embedded class
#Embeddable
#SuppressWarnings("serial")
public class JobVaccinationPK implements Serializable{
#ManyToOne
#MapsId("job_category_id")
#JoinColumn(name = "job_category_id", nullable=true)
private JobCategoryTypeMast jobCategoryMast;
#ManyToOne
#MapsId("vaccination_id")
#JoinColumn(name = "vaccination_id", nullable=true)
private VaccinationMast vaccinationMast;
#ManyToOne
#MapsId("screening_type_id")
#JoinColumn(name = "screening_type_id", nullable=true)
private ScreeningTypeMast screeningTypeMast;
//getters and setters
}
Service implementation class
#Override
public SearchResult<JobVaccinationDto> getJobVaccination(JobVaccinationDto dto)
throws VaccinationException {
List<JobVaccination> vaccDetails = jobVaccinationRepo.findAll();
if(vaccDetails == null) return null;
List<JobVaccinationDto> jobVaccinationDtos = new ArrayList<JobVaccinationDto>();
jobVaccinationDtos = convertToDto(vaccDetails);
return new SearchResult<>(jobVaccinationDtos.size(), jobVaccinationDtos);
}
Here am able to insert a null value for either jobCategoryId or screeningTypeId, just like below row. But when I'm trying to fetch the rows which have empty values, it returns null. I've tried to debug but I was not able to find the cause.
This is the generated hibernate query:
Hibernate:
select
jobvaccina0_.job_category_id as job_cate4_13_,
jobvaccina0_.screening_type_id as screenin2_13_,
jobvaccina0_.vaccination_id as vaccinat3_13_,
jobvaccina0_.default_yn as default_1_13_
from
cdcis.job_vaccination jobvaccina0_
Hibernate:
select
jobcategor0_.job_category_id as job_cate1_11_0_,
jobcategor0_.job_category_name as job_cate2_11_0_,
jobcategor0_.job_category_name_ar as job_cate3_11_0_,
jobcategor0_.screening_type_id as screenin4_11_0_
from
cdcis.job_category_mast jobcategor0_
where
jobcategor0_.job_category_id=?
Hibernate:
select
screeningt0_.screening_type_id as screenin1_21_0_,
screeningt0_.active_yn as active_y2_21_0_,
screeningt0_.mmpid_required_yn as mmpid_re3_21_0_,
screeningt0_.screening_type as screenin4_21_0_
from
cdcis.screening_type_mast screeningt0_
where
screeningt0_.screening_type_id=?
Hibernate:
select
vaccinatio0_.vaccination_id as vaccinat1_27_0_,
vaccinatio0_.vaccination_name as vaccinat2_27_0_,
vaccinatio0_.vaccination_name_ar as vaccinat3_27_0_
from
cdcis.vaccination_mast vaccinatio0_
where
vaccinatio0_.vaccination_id=?
Going with #Adam Michalik answer. As a work-around I've introduced a new primary key field in the table, as we can't handle a null in the composite key.
Composite IDs cannot contain null values in any of the fields. Since the SQL semantics of NULL is that NULL <> NULL, it cannot be determined that a primary key (1, 2, NULL) is equal to (1, 2, NULL).
NULL means "no value" in SQL and its interpretation is up to you on a case-by-case basis. That's why SQL and JPA do not want to make assumptions that NULL = NULL and that a primary key containing a NULL identifies a single entity only.
You may choose to use a synthetic, generated primary key instead of the composite business primary key to overcome that. Then, you'd always have a non-null, single-column PK and nullable foreign keys.
change the data type of particular row in entity from int to integer

Resources