Hibernate - unique column constraint being ignored - java

I have a MySQL table to hold tags (i.e. like those used here on Stack Overflow). It simply has an id (pk) and a tag column to hold the tag itself.
The annotated get methods for my Tag entity are shown below.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public int getId() {
return this.id;
}
#Column(name = "tag", unique = true, nullable = false)
public String getTag() {
return this.tag;
}
I am using a unique column constraint on tag as there should never be more than one row for a given tag. However, Hibernate appears to be ignoring this, i.e. I can save the exact same tag many times and it simply creates a new row rather than throwing an exception.
Am I missing something or should this be working?

From JavaDoc of UniqueConstraint (unique=true on #Colunm is just a shortcut):
This annotation is used to specify that a unique constraint is to be included in the generated DDL for a primary or secondary table.
So it does not seem to enforce uniqueness upon inserts. You should create a unique constraint in the database in any case.

You miss that this is only a information.
You should add also constraint on the column in database.

Related

How to map a table with composite key with one of the column being foreign key on Hibernate

I am having a hard time mapping the following database schema on Hibernate:
The tb_order is an existing table and it is already mapped on Java class. The tb_external_order_details table was recently created and it only have two columns. Both columns are part of the composite Primary Key. The tb_order_id column references to the id column on tb_order table and the external_order_id is just a loose id that doesn't references any column on database.
Please note that the database table and column names are not exactly equal to the Java classes and attributes names. e.g. the java class name is Order and the table name is tb_order. I just think this is important to note because the table/column names inferred by Hibernate may not match (also the table column is snake_case while properties on java classes will be camelCase).
I tried many solutions found here on stack overflow and none of them worked. Also, on the examples I found, no column of the composite Primary Key is a Foreign Key referencing another column of another table.
Try this:
#Entity
public class Order {
#Id
Integer id;
String type;
#OneToMany(mappedBy = "order")
Set<ExternalOrderDetails> externalOrderDetails;
}
#Entity
public class ExternalOrderDetails {
#EmbeddedId
ExternalOrderDetailsId id;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "tb_order_id", insertable = false, updatable = false)
Order order;
}
#Entity
public class ExternalOrderDetailsId {
#Column(name = "tb_order_id")
Integer orderId;
#Column(name = "external_order_id")
Integer externalOrderId;
}

Unique key auto generated in Spring Boot Entity

I have a primary key in my entity table which is autogenerated but now I want unique keys to be auto generated so how to do it
Please help me out.
#Entity
#Table(name = "director")
public class Director {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
//how to make this field auto generated like above one
#Column(name = "subid", unique=true)
private long sub_id;
My database table picture is here please refer
You can use timestamp or static AtomicLong counter as sub_id value. Try to define method with annotation #PrePersist in your entity class and your JPA provider will execute it before persisting an object.
Note: using timestamp in concurrent environment may lead to collisions and values won't be unique.
private final static AtomicLong subIdCounter = new AtomicLong(System.nanoTime());
#PrePersist
void sub_id() {
this.sub_id = subIdCounter.incrementAndGet();
}
After a short study it seems that that Hibernate supports the feature of generated values only with fields annotated with #Id. With #Id and default #generatedValue Hibernate creates - depending on the database and dialect used - appropriate way to generate the value of id field. usually this is something like creating a sequence and setting the column definition like (examples are from Postgres 12):
id bigint not null nextval('director_id_seq'::regclass)
Interesting thing is that this is done by issuing create statement like this:
create table director (id bigserial not null, primary key (id))
So, the column type bigserial actually generates sequence that is used to insert default value to the id column.
There are two options it you want to generate the value for column sub_id as it is generated to the column id. Both are database dependent.
Just create the sequence manually to the database and alter column sub_id to fetch the default value from the sequence.
OR
Change your column definition to use appropriate column type, like:
#Column(name = "subid", insertable = false,
nullable = false, unique = true, columnDefinition = "bigserial")
private long sub_id;
This will cause Hibernate to generate table like:
create table director (id bigserial not null, subid bigserial not null, primary key (id))
and result to a column like:
subid bigint not null nextval('director_subid_seq'::regclass)
But again: this is database specific stuff.
Also note: that JPA is aware only of the value that is stored to the id field. The subid is inserted to the database table but the sub_id field is not populated until entity is refreshed in its persistence context.

How to insert test data into H2 using sequence for default value, when Hibernate entity has GenerationType.SEQUENCE

When an entity is mapped with GenerationType.IDENTITY an sql insert into a h2 test db will have a default value for the id column pointing to a sequence. When mapped with GenerationType.SEQUENCE it won't have a default value.
We want to use GenerationType.SEQUENCE as it's more verbose and we want to show where everything comes from. During testing we've stumbled upon this problem - when using #Sql to insert a script into an embedded h2 database we get an error about a not null constraint.It also happens when we just execute the script as a native query.
This doesn't happen when using GenerationType.IDENTITY. The same script from above works properly.
If we use GenerationType.SEQUENCE and pass it the id by calling a next value function like this:
INSERT INTO example (id, name) (NEXTVAL('example_id_seq'), 'test');
the script will work without a problem.
Here is an example mapping an insert where the problem occurs:
#Entity
#Table(schema = DbSchemas.PUBLIC, name = DbTables.Example)
public class Example implements Serializable {
private static final long serialVersionUID = -8827852720381659873L;
private static final String EXAMPLE_GENERATOR = "example_generator";
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = EXAMPLE_GENERATOR)
#SequenceGenerator(name = EXAMPLE_GENERATOR, sequenceName = DbSequences.EXAMPLE_ID_SEQUENCE, allocationSize = 1)
private Integer id;
#Column(name = "name", nullable = false)
private String name;
}
INSERT INTO public.example (name)
VALUES ('test');
o.h.engine.jdbc.spi.SqlExceptionHelper : NULL not allowed for column "ID"; SQL statement:
I'd like to be able to call the insert shown above for entities mapped with GenerationType.SEQUENCE. The insert must use and increment the sequence described in the mapping.

JPA foreign key zero 0 value hibernate TransientPropertyValueException

I have found that hibernate (or mariadb) JPA does not seem to work with 0 values in a foreign key.
I have a parent class
class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PARENT_ID", unique = true, nullable = false)
private Integer parentId;
}
And a child class
class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "CHILD_ID", unique = true, nullable = false)
private Integer childId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "PARENT_ID", nullable = false)
private Parent parent;
}
So we have 2 rows in parent. parent_id=0 and parent_id=1
My problem is that I get an error when attempting to use parent with ID 0. i.e. This code
Parent p = entityManager.find(Parent.class, new Integer(0));
Child c = new Child();
c.setParent(p);
entityManager.persist(c);
Will fail with the error:
java.lang.IllegalStateException:
org.hibernate.TransientPropertyValueException: Not-null property
references a transient value - transient instance must be saved before
current operation : com.whatever.Child.parent -> com.whatever.Parent
But the following works fine:
Parent p = entityManager.find(Parent.class, new Integer(1));
Child c = new Child();
c.setParent(p);
entityManager.persist(c);
So I assume the PARENT_ID=0 somehow confusing JPA into thinking it is not a valid parent object.
Or is this actually a mariadb issue? Related to the fact that you have to change a session setting in order to insert 0's into AUTO_INCREMENT columns.
Is there any config or annotation I can do to make this work. Unfortunately we are putting JPA code on an existing system, so changing the PARENT_ID values is not a trivial task. (and everybody hates data conversion).
Any tips very much appreciated.
MariaDB/MySQL handle AUTO_INCREMENT thus: Numbers are 1 or greater; 0 is a valid sequence number. If JPA cannot live with those (and some other) limitations, JPA is broken. (Sorry, but I get irritated with 3rd party software that makes life difficult for MySQL users.)

How to create foreign key in Hibernate on Integer column

I have an entity in Java and I would like Hibernate to create a foreign key from an Integer field (since I don't have an object reference):
#Entity
public class Invoice {
...
#Column(nullable = true)
private Integer generatedBy;
...
I think I'd like to do something like this with an attribute:
#ForeignKey(name="FK_Invoice_GeneratedBy", references="UserTable.UserId")
#Column(nullable = true)
private Integer generatedBy;
What is the best way to achieve this? I would preferably not have to maintain these relationships in a separate file (if possible).
There doesn't seem to be a solution to this, thus accepting this as an answer.
There is a way to do it, but it is not very nice...
You can have your integer attribute, AND an object attribute mapped this way:
#Column(ame = "GENERATED_BY", nullable = true)
private Integer generatedBy;
#ForeignKey(name="FK_Invoice_GeneratedBy")
#JoinColumn(name = "GENERATED_BY", nullable = false, updatable = false, insertable = false)
private User generatedByUser;
You may keep no external access to your generatedByUser field, it will only show hibernate that there is a relationship. You can set the Integer field at will, when you load this object from DB later you'll have your user reference.
Again, not very pretty, but can be useful sometimes.

Categories