How to add a case insensitive JPA unique constraint? - jpa

I want to add a case insensitive unique constraint to a JPA entity.
Assume we have an entity employee which needs to have unique constraint for two columns NAME and PROJECT_TITLE where NAME is case insensitive.
Insertion of JoHn, PROJECT1 should cause unique constraint violation when a row JOHN,PROJECT1 already exists in database as JOHN, JoHn are same in our case.
The SQL for the above requirement is given below
ALTER TABLE employee ADD CONSTRAINT employee_name_unique
UNIQUE(LOWER(NAME),PROJECT_TITLE);

JPA doesn't support this kind of constraints. For a pure JPA solution, you'll have to use additional column. See the answer here: Case-insensitive JPA unique constraint?
#Entity Foo {
private value;
#Column(unique = true)
private valueLowercased;
#PrePersist #PreUpdate private prepare(){
this.valueLowercased = value == null ? null : value.toLowerCase();
}
}

Related

Unidirectional #OneToMany creates unique index at jon table which is not expected

I'm puzzled with JPA behaviour. Underlying DB is H2, I'm using SpringBoot and
jpa:
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.H2Dialect
Here is my entity:
#Entity
class ChildEntity{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
var id: Long = _
#OneToMany(fetch = FetchType.EAGER)
var parentEntities: java.util.Set[ParentEntity] = _
}
Here is what's created in DB:
CREATE INDEX "PUBLIC"."INDEX_C" ON "PUBLIC"."CHILD_ENTITY_PARENT_ENTITIES"("CHILD_ENTITY_ID");
CREATE PRIMARY KEY "PUBLIC"."PRIMARY_KEY_CA" ON "PUBLIC"."CHILD_ENTITY_PARENT_ENTITIES"("CHILD_ENTITY_ID", "PARENT_ENTITIES_ID");
-- why?
CREATE UNIQUE INDEX "PUBLIC"."UK_INDEX_C" ON "PUBLIC"."CHILD_ENTITY_PARENT_ENTITIES"("PARENT_ENTITIES_ID");
Why does it create unique index for PARENT_ENTITIES_ID on join table?
Jpa create join table automatically.
You can avoid extra join table issue using #JoinColumn .
The #JoinColumn annotation helps Hibernate to figure out that there is a Foreign Key column in parent.
This is to be expected, a one to many relationship connects two different entities together. JPA by default implements this when you don't do it explicitly. That is why you have a join table with the id for both entities.

jpa - postgresql unique index annotation definition makes unique constraint instead

I have the following annotation on the entity class. The database is postgresql. Unfortunately this creates the table with unique constraint, not unique index.
#Entity
#Immutable
#Table(
indexes = {
#Index(name = "MasterT75_unique_idx", columnList = "productdivision, giccode", unique = true)
}
)
public class MasterT75 extends ModelBaseValidate<MasterT75> {
...
}
i've found that, which describes well the difference of unique constraint and index.
https://www.postgresql.org/message-id/CAO8h7BJMX5V1TqzScTx2Nr1jH5iUFG8A071y-g1b_kdzpu9PDw%40mail.gmail.com
but i still don't understand, why hibernate doesn't create index as well (ok, not unique). because there is
uniqueConstraints = {}
parameter of #Table annnotation. so, if i want to make only unique constraint, i could use the latter one, but if i need index due to performance, i want to have index.

Does #OneToOne imply uniqueness?

I annotated my fields with only #OneToOne and when I check the database (generated using liquibase) saw that there are unique constraints on database columns.
Does this mean #OneToOne implies uniqueness just by itself, eg. one Building can only be in one city, and no other Buildings can be in the same city?
What do I do when I want to tell that there may be other other buildings in the same city?
add #JoinColumn(unique = false),
only use #JoinColumn(unique = false) without #oneToOne,
or use #ManyToOne?
or leave it without any annotations?
I don't want to put a Buildings field in the city class, because I wouldn't ever call city.getBuildings();. Does any of the below require a bidirectional reference?
class Building {
#OneToOne(optional = false)
City city;
}
class Building {
#OneToOne(optional = false)
#JoinColumn(unique = false)
City city;
}
class Building {
#JoinColumn(unique = true)
City city;
}
class Building {
#ManyToOne
City city;
}
The JPA specification says that for a bidirectional OneToOne relationship (2.10.1 Bidirectional OneToOne Relationships):
Assuming that:
Entity A references a single instance of Entity B.
Entity B references a single instance of Entity A.
Entity A is specified as the owner of the relationship.
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
Table A contains a foreign key to table B. [...] The foreign key column has the same type as the primary key of table B and there is a unique key constraint on it.
In case of unidirectional OneToOne relationship (2.10.3.1 Unidirectional OneToOne Relationships):
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
Table A contains a foreign key to table B. [...] The foreign key column has the same type as the primary key of table B and there is a unique key constraint on it.
If you have a City-Building relationship, then for any reasonable city it would be a OneToMany/ManyToOne relationship, since a given city can have multiple buildings, but a given building can be only in one city.

JPA Error : The entity has no primary key attribute defined

I am using JPA in my application. In one of the table, I have not used primary key (I know its a bad design).
Now the generated entity is as mentioned below :
#Entity
#Table(name="INTI_SCHEME_TOKEN")
public class IntiSchemeToken implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="CREATED_BY")
private String createdBy;
#Temporal( TemporalType.DATE)
#Column(name="CREATED_ON")
private Date createdOn;
#Column(name="SCH_ID")
private BigDecimal schId;
#Column(name="TOKEN_ID")
private BigDecimal tokenId;
public IntiSchemeToken() {
}
public String getCreatedBy() {
return this.createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Date getCreatedOn() {
return this.createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
public BigDecimal getSchId() {
return this.schId;
}
public void setSchId(BigDecimal schId) {
this.schId = schId;
}
public BigDecimal getTokenId() {
return this.tokenId;
}
public void setTokenId(BigDecimal tokenId) {
this.tokenId = tokenId;
}
}
Here In my project, eclipse IDE shows ERROR mark(RED colored cross) on this class and the error is "The entity has no primary key attribute defined".
Can anyone tell me, How to create an entity without primary key ?
Thanks.
You can't. An entity MUST have a unique, immutable ID. It doesn't have to be defined as a primary key in the database, but the field or set of fields must uniquely identify the row, and its value may not change.
So, if one field in your entity, or one set of fields in your entity, satisfies these criteria, make it (or them) the ID of the entity. For example, if there is no way that a user can create two instances in the same day, you could make [createdOn, createdBy] the ID of the entity.
Of course this is a bad solution, and you should really change your schema and add an autogenerated, single-column ID in the entity.
If your Primary Key(PK) is a managed super class which is inherited in an entity class then you will have to include the mapped super class name in the persistence.xml file.
Look at the bug report:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=361042
If you need to define a class without primary key, then you should mark that class as an Embeddable class. Otherwise you should give the primary key for all entities you are defining.
You can turn off (change) validation that was added.
Go to workspace preferences 'Java Persistence->JPA->Errors/Warnings' next 'Type' and change 'Entity has no primary key' to 'Warnning'.
In addition to http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#No_Primary_Key you can use some build-in columns like ROWID in Oracle:
Oracle legacy table without good PK: How to Hibernate?
but with care:
http://www.orafaq.com/wiki/ROWID
Entity frameworks doesn't work for all kind of data (like statistical data which was used for analysis not for querying).
Another solution without Hibernate
If
- you don't have PK on the table
- there is a logical combination of columns that could be PK (not necessary if you can use some kind of rowid)
-- but some of the columns are NULLable so you really can't create PK because of DB limitation
- and you can't modify the table structure (would break insert/select statements with no explicitly listed columns at legacy code)
then you can try the following trick
- create view at database with virtual column that has value of concatenated logical key columns ('A='||a||'B='||'C='c..) or rowid
- create your JPA entity class by this view
- mark the virtual column with #Id annotation
That's it. Update/delete data operations are also possible (not insert) but I wouldn't use them if the virtual key column is not made of rowid (to avoid full scan searches by the DB table)
P.S. The same idea is partly described at the linked question.
You need to create primary key ,If not found any eligible field then create auto increment Id.
CREATE TABLE fin_home_loan (
ID int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (ID));
Just add fake id field.
In Postgres:
#Id
#Column(name="ctid")
String id;
In Oracle:
#Id
#Column(name="ROWID")
String rowid;

Advanced JPA Mapping with Compound keys - OneToMany relationship

I am using JPA 1.0 and have following tables, namely, Type, Guide and Address (names simplified for clarity and highlighted in bold) It is a scenario, where in relationships between 3 tables is built on compound keys. Non key fields for every table are below the solid line.
Relationships
Type One --------> Many Guide
Guide Many <---------- One Address
Type
Code PK
Date1 PK
Name
Guide
Code FK
Date1 FK
Addr Identifier FK
Date2 FK
Value
Address
Addr Identifier PK
Date2 PK
Postal Code
(Pardon the formatting issue above)
I would like to start with Type table and unsing the compound key Code and Date1, get multiple rows (as a list) from Guide table. Then using Addr Identifier and Date2 from the row I want to get a single row in Address table. Please note these are reference tables and the data does not change, so there are no deletes or updates on any of these tables
I have tried this simple set of annotations that are returning empty list. (code is implified for clarity)
1)
#Entity
#Table(name = "Type")
public Class Type
#OneToMany(mappedBy = "type", fetch = FetchType.EAGER)
private List<Guide> listGuide;
getListGuide() {
return listGuide;
}
2)
#Entity
#Table(name = "Guide")
public Class Guide
#ManyToOne
#JoinColumns({#JoinColumn(name = "Code"),
#JoinColumn(name = "Date1") })
private Type type;
When I use getListGuide() I am getting an empty list.
Can you please suggest a solution?
I also need a mapping solution between Guide and Address entities.
Regards,