Logical delete and create with Hibernate - java

#Entity
#Table(name = "Country")
#SQLDelete(sql = "UPDATE Country SET date_deleted=NOW() WHERE code = ?")
#Where(clause = "date_deleted is NULL")
public class Country {
#Id
#Column(name = "code", nullable = false)
private String code;
#Column(name = "description")
private String description;
#Column(name = "date_deleted")
private Date date_deleted;
....
}
When I logic delete an Entity in the database with the code 'U1' and after, I created a new Entity with the same code 'Ü1', occurs an exception "duplicate entry". Has Hibernate an annotation to solve this problem?
edit:
The Error when I insert a new entity with the same code is this:
org.postgresql.util.PSQLException: ERROR: duplicate key value violates
unique constraint "country_pkey" Detail: Key (code)=(AA) already
exists.
The table is:
CREATE TABLE public.country(
code bpchar(2) NOT NULL,
description bpchar(50) NULL,
date_deleted timestamp NULL,
CONSTRAINT country_pkey PRIMARY KEY (code),
CONSTRAINT constraint_country UNIQUE (date_deleted, code) -- I add this constraint
);

Since you manage the code column and you can have multiple entries with the same code, one solution would be to have an id column that is autogenerated.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
This will enable you to delete an object with the code 'U1' and add another one with the same code.
You can check this great tutorial: https://vladmihalcea.com/the-best-way-to-soft-delete-with-hibernate/

Related

Hibernate 5.3.10 one-to-many insertable = false behaves differently from Hibernate 5.2.1

I have two classes one contained within the other. SchoolClass and Student
When persisting them in Hibernate 5.2.1 everything works as expected, but when persisting in Hibernate 5.3.10 I have to remove or set insertable = trueto get the same result otherwise I get exception.
What I'm looking for is a confirmation that the behavior of hibernate has changed. When where and why...
I have not been able to find any documentation about this at all.
jdbc.spi.SqlExceptionHelper - NULL not allowed for column "schoolClassId"; SQL statement:
insert into tStudent (studentId, name) values (null, ?)
org.hibernate.exception.ConstraintViolationException: could not execute statement
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement.
#Entity
#Table(name = "tSchoolClass")
#AutowiringTarget
public class SchoolClass {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "schoolClassId")
private Long id;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "schoolClassId", nullable = false, insertable = false, updatable = false)
private List<Student> students;
#Entity
#Table(name = "tStudents")
#AutowiringTarget
public class Students {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "StudentId")
private Long id;
H2 database.
CREATE TABLE tSchoolClass (
schoolClassId int IDENTITY(1,1) NOT NULL,
CONSTRAINT PK_tSchoolClass PRIMARY KEY (schoolClassnId));
CREATE TABLE tStudents (
studentId int IDENTITY(1,1) NOT NULL,
schoolClassint NOT NULL,
CONSTRAINT PK_tStudents PRIMARY KEY (studentId),
CONSTRAINT FK_tStudent_tSchoolClass FOREIGN KEY (schoolClassId) REFERENCES tSchoolCLass (SchoolClassId));
The exception NULL not allowed for column "schoolClassId" is clearly saying schoolClassId cannot be null.
Its the nullable = false property, that would enforce the not null constraint on the column schoolClassId which can be translated to schoolClassId bigint NOT NULL in the student create table.
The insertable=true on schoolClassId column would mean the column is included in the insert query. So whenever an instance of SchoolClass is persisted, the associated Student instances will be persisted too. The student entity insert will include the SchoolClassId column , its value referencing to SchoolClass id's instance, which is not null in this case.
So in short, anytime the column schoolClassId is null, the constraint violation will be thrown, so keeping insertable=false, you would need to set nullable = true if you have to get rid of the violation.

Correct way to check for foreign key constraint violation in Hibernate?

I have two tables that already exist inside postgres, lets call the Table A and Table B. One column of Table B has a foreign key constraint in that it has to be the primary key of Table A. Thus there is a many-to-one relationship between B and A, where multiple records in Table B correspond to one record of Table A.
The Entity for both these tables are defined as follows.
public class TableA implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
#Column(name = "phone_number")
private String phoneNumber;
}
TableB's entity is defined as follows:
public class Shots implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private Long itemId;
#Column(name = "user_id")
private Long userId;
}
Where userId is the foreign key mapping to the primary key user_id in Table A.
These constraints have already been defined in the underlying postgres database, so i didn't consider using the #ManyToOne annotation relationship (still trying to wrap my head around it).
The way i currently handle the case when a foreign key constraint violation occurs is by doing the following:
try {
tableBrepository.save(newShot);
} catch (ConstraintViolationException ex) {
logger.error("Violating foreign key constraint" + ex.getMessage());
}
My question is, is there a better way to check for this violation? Is there anything i can do to generally better structure the foreign key constraint in Spring Data JPA?
Thus there is a many-to-one relationship between B and A, where multiple records in Table B correspond to one record of Table A.
This kind of stuff in JPA entities is handled with #ManyToOne annotation. You usually do not refer to any id field directly but tell JPA what there should be. So in your class TableB (or should I call it... Shots?) should be something like:
#ManyToOne
private TableA tableA;
// and get rid of this
// #Column(name = "user_id")
// private Long userId;
And optionally - so not necessarily - you could have, in your TableA:
#OneToMany
private List<TableB> tableBsOrShouldICallYouShots;
I am not sure what is your actual problem but when setting and referring to id fields directly might cause your difficulties.
Now if you -for example- use repository to find some TableB you can then after that just do
tableB.getTableA()
And when saving you would before that do:
tableB.setTableA(somSortOftableA);
// so not tableB.setUserId(someLongIdFOrtableA);
Now the point is that there is no problem with referential integrity because you do not need to know any IDs and you cannot set any wrong ID. Unless you first need to fetch TableA by id before setting it to TableB but in that case you would still not set any IDs.

Hibernate: #OneToOne not producing "one to one" relationship in database

I'm relatively new to JPA and Hibernate and am trying to see how the #OneTo One annotation works, let's say I have an entity "Task" with the following relation:
#OneToOne
#JoinColumn(name = "manager_id")
private Manager manager;
And there's the entity "Manager":
#Entity
#Table(name = "manager")
public class Manager {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
public Manager() {
}
When I run the test file along with the "hibernate.hbm2ddl.auto" set to "update" I get a Many to One relation in the database (as you can see, there is no unique constraint of any kind that'd make it a one to one relation):
CREATE TABLE IF NOT EXISTS `timesheet`.`task` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`completed` BIT(1) NOT NULL,
`description` VARCHAR(255) NULL DEFAULT NULL,
`manager_id` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `FK3635851B178516` (`manager_id` ASC),
CONSTRAINT `FK3635851B178516`
FOREIGN KEY (`manager_id`)
REFERENCES `timesheet`.`manager` (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
To be sure of this I tried adding two records with the same manager id and were indeed added, I also tried setting the unique constraint like "#Table(name = "Task",uniqueConstraints = #UniqueConstraint(columnNames =..." but no luck.
So Why is this happening and what's exactly the pros of using #OneToOne annotaion if no application logic is applied to validate this?
Also, Is there any chance that Hibernate is not able to do the DDL generation properly?
(I know that generation of schemas through hibernate is only meant for testing)
In a unidirectional relationship you will get the expected unique constraint if you mark it as "optional=false". You also get it if you set the join column explicitly as unique, of course.
So either
#OneToOne(optional=false)
#JoinColumn(name = "manager_id")
private Manager manager;
or
#OneToOne
#JoinColumn(name = "manager_id", unique=true)
private Manager manager;
So why do you need to mark it as not optional?
My guess is that, when a value is optional, the column can contain many null values, but in many databases this can not be done when a unique constraint is present. You can do it in MySQL though, so maybe the Hibernate generator is not taking the database into account in this case (a bug?).
See a discussion about MySQL handling of nulls here.
I had this issue too and I just needed to add the referenced column so I can get a generated table:
#Entity(name = "news")
public class News extends BaseEntity {
#Column(length = 500)
private String title;
#Column(length = 2000)
private String description;
#OneToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "file_id", referencedColumnName = "id", unique = true)
private Picture picture;
}

Spring Data JPA "null value in column xxx violates not-null constraint" on serial column with postgresql

My entity has a mapOrder field which I want auto-increment like below:
#Entity
public class Map{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(columnDefinition = "serial")
private Long mapOrder;
//.......
}
The sql generated seems good:
CREATE TABLE map
(
id bigserial NOT NULL,
map_order serial NOT NULL,
...
)
But when I save it with Spring Data JPA's repository, like this:
Map m=new Map();
repo.save(m);
will give me exception:
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "map_order" violates not-null constraint
Any ideas?
Try changing your code to this:
#GeneratedValue(strategy = GenerationType.SEQUENCE)
Reference: https://stackoverflow.com/a/29028369
#GeneratedValue works with identifiers and you can't use it with regular fields.
You can, for example, use some object for sequences (with any key generation strategy):
#Entity
public class JpaNoPkSequence {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id", nullable=false, updatable=false)
private Long id;
}
For using the strategy GenerationType.SEQUENCE the sequence should be created in the database:
CREATE SEQUENCE JPA_PK_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
ALTER SEQUENCE "JPA_PK_SEQ" OWNER TO something;
This should be specified in the key definition. You should also add a one-to-one relationship with the object that you will use to obtain sequences for regular fields:
#Entity
public class Map {
#Id
#SequenceGenerator(name="jpaPkSeq", sequenceName="JPA_PK_SEQ", allocationSize=1, initialValue = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jpaPkSeq")
#Column(name = "id", nullable=false, updatable=false)
private Long id;
#OneToOne
private JpaNoPkSequence sequence;
...
}
Hope this helps.
I had the same problem. In my case, the error was in creating the table.
You need to use SERIAL type instead of others (at least in Postgres).
Before (bigint type)
CREATE TABLE IF NOT EXISTS sample_table
(
id BIGINT NOT NULL,
...
);
After (serial type)
CREATE TABLE IF NOT EXISTS sample_table
(
id SERIAL NOT NULL,
...
);
The id in the entity class looked like this:
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
This question is full of bad advice. The problem is the second column with a serial value. The problem is that Hibernate explicitly inserts a null there. You have to tell it not to.
#Column(columnDefinition = "serial", insertable = false)
private Long mapOrder;

How to resolve hibernate table creation while its says unsuccessful creating table

I have a domain class name DataList
#Entity
#Table(name = "list_data")
public class ListData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "sys_id")
private String sysId;
#Column(name = "name")
private String name;
#Column(name = "detail")
private String detail;
#Column(name = "values")
private String values;
//getters and setters
}
I have some others domain class..
I'm using hibernate 3.6 everything alright.
but somehow Im unsuccessful while creating this table.
2012-02-25 03:31:52,166 ERROR SchemaExport:274 Unsuccessful: create table list_data (id >integer not null auto_increment, detail varchar(255), name varchar(255), sys_id varchar(255), >values varchar(255), primary key (id))
2012-02-25 03:31:52,167 ERROR SchemaExport:275 You have an error in your SQL syntax; check the >manual that corresponds to your MySQL server version for the right syntax to use near 'values >varchar(255), primary key (id))' at line 1
I know my hibernate configuration is fine, I have some other domain class, they are working just fine.
I think that you cannot use values as a column name since it is a MySQL keyword (INSERT INTO ... VALUES() ).

Categories