The default entity from enum - java

I have property in Enum:
#Basic
#Column(name = "payment_status", columnDefinition = "varchar(32) default 'ENTERED'", nullable = false)
#Enumerated(EnumType.STRING)
private PaymentStatus paymentStatus;
I want to get the default value for a field from enum
I have error:
org.hibernate.PropertyValueException: not-null property references a null or transient value
The field cannot be null
The error is when I want to create an object and save in the database without entering this field (PaymentStatus)
EDIT:
#Basic
#ColumnDefault(value = "ENTERED")
#Column(name = "payment_status", nullable = false)
#Enumerated(EnumType.STRING)
private PaymentStatus paymentStatus = PaymentStatus.ENTERED;
Why is it not working?

default 'ENTERED' tells the database to use value 'ENTERED' if the column is not included in the INSERT statement. Since the column is in the class, JPA will always include it in the INSERT statement.
To make JPA fill in the default value, simply assign it with an initializer, so it has that value until replaced by you (calling setter method), or replaced from database (when reading from there).
private PaymentStatus paymentStatus = PaymentStatus.ENTERED;

If you have defined a default in the database and if you want to leave it to the database to set the default value then you need to prevent a null being inserted to that column in the SQL insert statement
You would then need to look at what your JPA provider offers in that area. In Hibernate, for example, you would simply annotate your Entity with #DynamicInsert:
https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/DynamicInsert.html
For inserting, should this entity use dynamic sql generation where
only non-null columns get referenced in the prepared sql statement?
It should be noted that defining a default in the JPA annotations (as you have done) only has any effect on DDL generation i.e. if you are having your JPA provider generate or update your schema and has to effect on any insert or update operations.

Related

Skip a column on INSERT with JPA

I have a JPA/EclipseLink model with multiple parameters whose values are set as defaults by the PostgreSQL database. Specifically, I have:
An id column of type SERIAL which auto-increments when new rows are added
A created_at column of type TIMESTAMP which defaults to now()
My model looks like this:
import javax.persistence.*;
#Entity
#Table(name="entity")
#NamedQuery(name="Entity.findAll", query="SELECT e from Entity e")
public class Entity {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="created_at")
private java.sql.Timestamp createdAt;
// constructor, getters, and setters
}
When I try to insert a row with persist() in javax.persistence.EntityManager, the insertion fails because a NULL value is being set for the created_at column. Rather than inserting NULL, I want to simply not insert anything into that column and allow PostgreSQL to set it to now(). Essentially, I would like to use #GeneratedValue on createdAt, but that annotation is only for primary keys and cannot be used on multiple attributes. Is there another annotation I can use that will accomplish this?
You may want insertable=false in the Column annotation:
#Column(name="created_at", insertable=false)
private java.sql.Timestamp createdAt;
From: http://docs.oracle.com/javaee/5/api/javax/persistence/Column.html
boolean insertable
(Optional) Whether the column is included in SQL INSERT statements generated by the persistence provide
You can add #DynamicInsert on your entity class. The insert statement will include null fields when without this annotation. Also, you can add # DynamicUpdate when executing the update statement.
#Entity
#DynamicInsert
#DynamicUpdate
#Table(name="entity")
#NamedQuery(name="Entity.findAll", query="SELECT e from Entity e")
public class Entity {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="created_at")
private java.sql.Timestamp createdAt;
// constructor, getters, and setters
}
As mentioned in another answer, JPA providers will set the value on insert if you have it marked as insertable=true (the default). This occurs with many providers even if null as it allows statement reuse.
Just excluding it from the statement though might not be what you want, as to have the value in your entity (and in the cache) will require refreshing. EclipseLink though has #ReturningInsert which allows EclipseLink to update the entity with the value in the database. Unfortunately support is only for Oracle - other databases require use of stored procedures to return run the insert and return the value.

Best practice regarding null value in entity objects

I am reviewing the code of a colleague. In his entity object he has set nullable = false and he is also checking in the setter that the value to set is not null.
Is this useful? In any case, the nullable = false will throw an exception at some point.
(The checkArgumentNotNull will throw an illegal argument exception if the value is null.)
private TypeChampMaterielDefaillant typeChamp;
#Column(name = "TYPE_CHAMP", nullable = false, length = 30)
#Enumerated(EnumType.STRING)
public TypeChampMaterielDefaillant getTypeChamp() {
return typeChamp;
}
public void setTypeChamp(TypeChampMaterielDefaillant typeChamp) {
checkArgumentNotNull(typeChamp, "typeChamp");
this.typeChamp = typeChamp;
}
EDIT
So if I understand correctly nullable=false only apply to schema generation, thus if the database is not generated with the current entity it will be possible to persist a null value
Parameter nullable = false will happen on database operation (you won't be able so persist entity with this value equals to null). Additional check in setter is useful because you'll get exception earlier (during setter invocation) but not necessary.
I would say nullable = false is used for schema generation, not for validation from jpa(unless some third party library uses for validating before persist)
JPA 2.1 Specification:
11.2.2.1 Column
The following elements of the Column annotation are used in schema generation:
name
unique
nullable
columnDefinition
table
length (string-valued columns only)
precision (exact numeric (decimal/numeric) columns only)
scale (exact numeric (decimal/numeric) columns only)
See section 11.1.9 for the rules that apply to these elements and column creation. The AttributeOverride annotation may be used to override column
mappings.
As you can see jpa spec talks nothing about validation, unless hibernate/or some third party does. I am not positive either about hibernate validator for that nullable = false statement.
You better use some validator framework or use #PrePersist, #PreUpdate annotations.
Setter validation also bad, how about if user does not call it at all ?
No, this is very bad practice. A Hibernate-Bean is a bean, any kind of intelligent setter is an workaround anyway.
Use the annotations instead like #NotNull!
Use asserations if you realy need something that is thrown in test/devel!

Stating table column properties with Hibernate

I am using Hibernate in my project. I have a database schema already created.
I see in the tutorials online
#Column(name = "STOCK_ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
the columns properties like unique nullable etc are being used. My question is do I need to
specify these properties when I already have a db schema prepared prehand with the columns being given all the not null and all the checks while creating the tables?.
No you don't, only the required parameters in the Hibernate annotations have to be filled in. But the optional parameters frequently have default values. Your DB will have to be compatible with the default values, else you will have to fill in the values you use.
unique is only used when generating the database schema from the JPA annotations. nullable = false allows JPA to be more efficient: if the value is null when it writes the entity to the database, it doesn't even have to execute the insert/update query and can throw an exception immediately.
Even if that's not absolutely necessary, I like having these informations in the mapping for documentation purposes. Being able to know immediately from the code, without looking at the database schema, that an attribute is nullable or not, is useful.

Hibernate self-reference entity as non null column

I'm using Hibernate 5 and Spring 3.2.4. I'm deigning a User entity in which I want to include a reference to the user that has created the entity - so a self reference. The self reference itself isn't too problematic, but I want to specify the field as non null. Is this possible? How do I create the first entry in the DB if the field is non null as there referenced entity does not already exist?
Ex:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long User.id;
#NotNull
private String username;
private String password;
#NotNull
private User user;
// getters and setters omitted for brevity
}
If I try:
User u = new User();
u.setUsername("username");
u.setCreatedBy(u);
and try to persist u, I get the following error message:
Caused by: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.domain.User.createdBy -> com.domain.User
I understand that Hibernate is complaining that it cannot reference a transient instance (ie: u), but I cannot persist u unless I have an non-null User that I can reference. But in an empty DB, there is no such entry.
Is this kind of configuration impossible to do? Or is there a way around this?
I don't understand this Roo annotations and I don't use Hibernate-specific annotations, only JPA. I don't have any issues with self references. But I have some hints fo you:
As mentioned before, use #ManyToOne annotation.
AFAIK, #NotNull annotation (or nullable field in #Column) does not affect mapping, only DDL generation. I don't use DDL generation from domain model, do I never specify this. Instead I use optional field of #ManyToOne.
What identifier generation strategy you use? If autoincrement, self-references are impossible with NOT NULL constraint. So either use sequence-based identifier generator or remove constraint. I would use first.
As I mentioned, set optional field of #ManyToOne to false, when you have NOT NULL constraint. Otherwise Hibernate attempts to make two queries: insert with createdBy_id set to NULL and then update createdBy_id. And the first query fails with NOT NULL contraint enabled.
I found a solution for this. You must use a Sequence generator for your ID (instead of the default auto generated IDs). Then it works.
#Entity
public class UserModel {
/**
* We must use a sequence for generating IDs,
* because of the self reference .
* https://vladmihalcea.com/2014/07/15/from-jpa-to-hibernates-legacy-and-enhanced-identifier-generators/
*/
#Id
#GeneratedValue(generator = "sequence", strategy=GenerationType.SEQUENCE)
#SequenceGenerator(name = "sequence", allocationSize = 10)
private Long id;
/** reference to a parent user, e.g. the manager */
#ManyToOne(optional = false)
#NotNull
UserModel parentUser;
[...]
The reason is the following: When Hibernate tries to insert a new User it also tries to validate the reference to the parentUser. But that will fail, for the first user we want to insert, or will also fail when a user references himself.
But when IDs are generated with a sequence, then the new/next ID is already known at the time of insert.
The field createdBy of u can not be null because of the annotation #NOTNULL. But u referes to itself and its not persist before saving it.
You can set another persisted User for u, not itself.

What are reasonable scenarios to use GenerationTime.INSERT in the Hibernate #Generated annotation?

I wonder about the behavior of Hibernate regarding fields annotated with #Generated(value=GenerationTime.INSERT) when I do an update on the entity.
I have an entity with the following fields (which are populated by database triggers, the first one on insert, the second one on insert and for each update):
#Generated(value = GenerationTime.INSERT)
#Column(name="created_by", insertable = false, updatable = false)
private String createdBy;
#Generated(value = GenerationTime.ALWAYS)
#Column(name="updated_by", insertable = false, updatable = false)
private String updatedBy;
When I load an entity from the database, both fields are populated - as expected
When I receive the entity back from the web-ui, both fields will be null - as expected since they weren't bound to any form field.
After calling
sessionFactory.getCurrentSession().saveOrUpdate(object);
the createdBy field will be null but the updatedBy field will be set to the correct value (created by the database).
So the entity no longer contains the values from the database - an undesired behavior.
For my situation can solve the issue
- I could change the GenerationTime to ALWAYS - but this seems to be confusing since createdBy is really only set when the entity is inserted.
- I could do a refresh on the entity - but I would like to avoid the extra call.
What is the best practice for the described situation? Are there better options that avoid an explicit call of refresh and don't obscure the semantic of fields modified only on insert?
What are reasonable scenarios to use #Generated(value=GenerationTime.INSERT)?
can you try like this.
#Temporal(TemporalType.TIMESTAMP)
#Generated(GenerationTime.ALWAYS)
#Column(name="create_date", insertable=false,updatable=false)
private Calendar createDate;

Categories