Play Framework [1.2.4]: unique constraints for an entity - jpa

I am using playframwork with mySQL for my web application. I have a table which requires an unique_constraint on multiple columns.
I have the entity defined as follows...
package models;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
#Entity
#Table(name = "table",
uniqueConstraints = { #UniqueConstraint(columnNames =
{ "col_1", "col_2" }) })
public class Table extends Model{
#ManyToOne
#JoinColumn(name = "col_1")
public Column1 col1;
#ManyToOne
#JoinColumn(name = "col_2")
public Column2 col2;
#ManyToOne
#JoinColumn(name = "col_3")
public Column3 col_3;
}
Column1 and Column2 are different entities with relationship with
Table entity.
When i try to insert data with duplicate "col_1 and col_2" values as
shown below i am getting no error. The data is inserted fine in the
table (MySQL).
Table table1 = new Table();
table1.col1 = new Column1("1");
table1.col2= new Column2("2);
table1.col3= new Column3("3");
table1.save();
Table table2 = new Table();
table2 .col1 = new Column1("1");
table2 .col2= new Column2("2);
table2 .col3= new Column3("3");
table2 .save();
Do i need to create unique_constraint manually on the table?
Please help me understand, if i have anything missing in the above
implementation
Thanks,
Karthik

If the table is not created by JPA, yes, you'll have to manually create the unique constraints for them to be taking affect.
If the table does not pre-exist, JPA should create the constraint for you.
JPA does not create a constraint on an already existing table.

Related

PanacheEntityBase setting by default id as not nullable, even if specifying the reverse

I have an java class set as entity, which is defined as follow:
package com.redhat.bvbackend.player;
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.Entity;
import javax.persistence.SequenceGenerator;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import com.redhat.bvbackend.team.Team;
#Entity
public class Player extends PanacheEntityBase {
#Id
#Column(name = "player_id", nullable = true)
#SequenceGenerator(name = "playerSequence", sequenceName = "playerIdSequence", allocationSize = 1, initialValue = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "playerSequence")
public Long id;
public String name;
public String familyName;
public int age;
#OneToMany(mappedBy = "name", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
public List<Team> teams;
#NotNull
#Size(min = 1, max = 1, message = "Handed must be either Left or Right")
public String handed;
}
My class extends PanacheEntityBase and I am setting the column to allow nullable entries, and I am creating a generator to automatically increment the player_id for each new entry. My thought is that if a generator is defined, I shouldn't need to set the nullable since the generator already have an initialValue specified. Actually if I see the #column reference or not, it doesn't change I get always the same output. See below.
I would like to create an player as follow:
INSERT INTO player (age,familyname,handed,name) VALUES (25,'foo','x','y');
without the need to specify the id. However when I do so I get:
ERROR: null value in column "player_id" violates not-null constraint
DETAIL: Failing row contains (null, 25, foo, x, y).
What am I doing wrong? Thanks in advance.
Though you have a sequence generator created for that ID as playerIdSequence your column does not have a default value set.
The #GeneratedValue itself will be used within the panache insert sequence itself, and it will set the value of the ID when building the SQL request.
If you want to be able to automatically assign your ID when running raw SQL requests to the database yourself, you should assign a default value to something like nextval('playerIdSequence'). This way, it will get the next number in the sequence.
You can change the table like this:
alter table public.player alter column player_id set default nextval('playerIdSequence');

JPA #Index annotation doesn't work

I want to add index to one of the columns in my table, but it always complained that column is not found, I checked the table, the column exists!
If I removed that index annotation from the entity object, it works fine.
If I change columnList to the field name, eg, columnList="reportOwnerId", it fails with same message: 'database column not found'
how can i make it work? thanks
here is my entity class:
import javax.persistence.Id;
import javax.persistence.Index;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Entity
#Getter
#NoArgsConstructor
#Table(name = "schedules",
indexes = { #Index(name = "SCHEDULE_OWNER_GUID_INDEX", columnList = "report_owner_guid")}
)
public class ReportSchedule extends AbstractTimestampEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Setter
#Column(name = "report_owner_guid", nullable = false, unique = false)
private String reportOwnerId;
...
and here is the error message:
Caused by: org.hibernate.AnnotationException: Unable to create unique key constraint (report_owner_guid) on table schedules: database column 'report_owner_guid' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
at org.hibernate.cfg.Configuration.buildUniqueKeyFromColumnNames(Configuration.java:1682)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1457)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
Later I figured out it might caused by the fact that column is not unique, and I updated the index to :
#Table(name = "schedules",
indexes = { #Index(name = "SCHEDULE_OWNER_GUID_INDEX", columnList = "report_owner_guid", unique = false)}
)
it still failed with the same error.

JPA wrong number of columns

Using Spring Framework JPA I have an Entity
#Entity
#IdClass(ComposityKey.class)
#Table(name="table1")
public class Table1 {
#Id
#Column(name="tableId)
private int tableId;
#Id
private int tableId2;
...
}
2nd Entity contains a reference to Table1
#Entity
#Table(name = "table2")
public class Table2 {
#ManyToOne
#JoinColumn(name="tableId")
private Table1 table1;
}
Now I'm getting the obvious AnnotationException
A Foreign key refering Table1 from Table2 has the wrong number of column. should be 2
Table2 doesn't have tableId2 as a value
So how can i JoinColumn on a Composite Key?

Failing me to apply update query scripts in Cuba Platform

I am receiving this error when I apply this update database script.
default expression needed in statement [alter table WATERSCHEME_LOCATION add column LGA_ID integer not null ]
LGA_ID is a column that is associated with Location Table, LGA_ID is set to mandatory. But when I remove the mandatory, I can update the scripts.
#Table(name = "WATERSCHEME_SCHEME_C")
#Entity(name = "waterscheme$Scheme_c")
public class Scheme_c extends BaseIntegerIdEntity {
private static final long serialVersionUID = -5886267876250540580L;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "LGA_NAME_ID")
protected Lga lgaName;"""
this is my location table
/*
* Copyright (c) 2016 water-scheme
*/
package com.company.waterscheme.entity;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import com.haulmont.cuba.core.entity.BaseIntegerIdEntity;
import com.haulmont.chile.core.annotations.NamePattern;
/**
* #author samuel.thampy
*/
#NamePattern("%s|locationName")
#Table(name = "WATERSCHEME_LOCATION")
#Entity(name = "waterscheme$Location")
public class Location extends BaseIntegerIdEntity {
private static final long serialVersionUID = 8201565825728955033L;
#Column(name = "LOCATION_NAME", nullable = false)
protected String locationName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "LGA_ID")
protected Lga lgaId;
public void setLocationName(String locationName) {
this.locationName = locationName;
}
public String getLocationName() {
return locationName;
}
public void setLgaId(Lga lgaId) {
this.lgaId = lgaId;
}
public Lga getLgaId() {
return lgaId;
}
}
Can somebody help me why I should set a default value?
Probably table you are changing is not empty, is it? BTW, which database is used?
You can't just add a non-null column to a table which already has some data in it.
Usual steps how it is usually achieved:
1) Add nullable column
2) UPDATE all existing rows in the table so that they receive some value.
3) Execute "alter table alter column set not null" to set column as non-null.
Another way is to modify your update script. If you use the CUBA Studio then go the Entity panel and press Generate DB scripts as it is shown in the picture below:
Then you will see all database scripts. Find proper scrip that has
alter table WATERSCHEME_LOCATION add column LGA_ID integer not null
statement and add
DEFAULT {DEFAULT_VALUE}
clause, where you can specify id of the Location entity that should be set while altering for null values. Now you can update your database!
P.S. Note that if you run this script on another database it may cause FOREIGN KEY constraint exception, because Location with the default id doesn't exist. To avoid this you can add another statement to the same update script, so that if the default location doesn't exist in your database then insert it with the specified id.

Use different field for column in subclass of EJB entity

I need some help with the design of my entities.
I have a single table MyEntity with the following columns:
id | column1 | column2 | type
And the entity looks like:
#Entity
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING, name = "type", length = 255)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class MyEntity {
#id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
}
I would like to be able to have several entities inheriting from MyEntity and where each entity define how they use the columns column1 and column2. I would like to just use one single table for all my subclasses, and use the discriminator.
Like this (the following doesn't work):
#Entity
#DiscriminatorValue("TestClass1")
public class TestClass1 extends MyEntity {
#Column(name = "column1");
private long testField1;
#Column(name = "column2");
private long testField2;
}
#Entity
#DiscriminatorValue("TestClass2")
public class TestClass2 extends MyEntity {
#Column(name = "column1");
private long anotherName1;
#Column(name = "column2");
private long anotherName2;
}
I know this approach is not working, so I am reaching out to you guys for help.
EDIT
I have added the inheritance strategy to the MyEntity class, but SINGLE_TABLE is default, so it shouldn't make a difference.
I have tried two approaches:
1) If I don't put two fields MyEntity named column1 and column2, then I cannot create any named queries in MyEntity such as:
#NamedQuery(name=TEST, query="select m from MyEntity m where m.column1 = :val")
I get the following exception:
Caused by: org.hibernate.QueryException: could not resolve property: column1 of: com.test.MyEntity
I would really prefer to specify the queries in MyEntity and not in the subclasses.
2) If I put the fields (column1 and column2) in MyEntity, then I get the following exception:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.test.TestClass1 column: column1 (should be mapped with insert="false" update="false")
and I don't know how to move on from here. I'm not sure how to do the mapping, which is suggested in the exception message.