Define default column value with annotations in Hibernate - java

I know there are plenty of these questions here on SO and also on the net, but all the answers suggest using columnDefinition which is database specific and hence not applicable for me because the system I'm working on needs to run on different databases.
I found this hibernate issue where someone requested this feature for annotations. The issue has been closed saying that another issue will cover that functionality. The second issue apparently added annotation #Generated and also some others, but I couldn't find any documentation on how to define the default column value with those new annotations.
So my question is: Does anyone know how can I define a default column value with annotations (and NOT using columnDefinition)?
Edit: to further clarify my problem: When I add a new not null column, I need Hibernate to update the existing schema (add the new column to the respective table). But since the column is non null, the database cannot create the column without specifying the default value (if there are already some rows in the table). So I need to instruct Hibernate to issue the following DDL statement: ALTER TABLE my_table ADD COLUMN new_column VARCHAR(3) DEFAULT 'def', but it has to be independent of the used database.

I don't think you need any documentation, the java docs are self explaining.
If I understand you correctly you need a way to set a default value for a field. If yes please see the following code snippet.
#Entity
#Table(name = "my_entity")
public class SomeEntity extends BaseEntity {
public static final class MyValueGenerator implements
ValueGenerator<String> {
#Override
public String generateValue(Session session, Object owner) {
return "This is my default name";
}
}
#Basic
#Column(name = "name", insertable = true, updatable = true, nullable = false, length = 255)
// This will add a DDL default
#ColumnDefault("'This is my default name'")
// This will add a runtime default.
#GeneratorType(type = MyValueGenerator.class)
private String name;
// getters and setters
}

Following is working for me.
#ColumnDefault("'0.0'")
#Column(name = "avgRating")
private float avgRating;

Related

How to enforce unique field with MongoDB in Spring

I have a pojo with two fields that need to be unique, the id and the email. So I added the #Indexed(unique = true) annotation to the necessary fields like so
public class User {
#Id
#Indexed(unique = true)
private String id;
#Indexed(unique = true)
private String email;
private int money;
I then tested it out and it was not enforced. So I googled about and I found a previous answer here - Spring Data: Unique field in MongoDB document and subsequently deleted the collection, added spring.data.mongodb.auto-index-creation=true to my application.properties file and tried again.
However, the unique field still isn't enforced! I see there is another answer using ensureIndex() but it also has a great comment that was never answered- Why do we need to use the annotation if all the work is done on mongoTemplate?
So since the question is old enough that apparently the only working answer is depreciated (the new way is using createIndex()), I thought it was time for a new version. Is it possible to require a column in a mongo collection to be unique from Spring Boot?

The default entity from enum

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.

hibernate - select/load only field

I want to add a field in a Hibernate table-mapped/entity class.
I want this field to not be mapped to an actual table column, and I want Hibernate not to try to insert/update it to the DB.
But I want to be able to load this field via a custom select in the DAO e.g. via
query.addEntity(getPersistentClass().getName());
The closest I got to this was by making the field #Transient,
but then even the select does not load its value. So this is not
quite what I need.
Is this possible at all and if so how?
Well if i understand what you are trying to do well then i think the solution like this
#Column(name = "{name of column}", updatable = false)
In this way the hibernate will not try to update this column once the object created
Your getter must be a bit smarter.
For exemple you can the HibernateCallback interface from spring like that:
public String getName(Session session) {
return new HibernateCallback<String>() {
#Override
public String doInHibernate(Session session) throws HibernateException {
return session.createSQLQuery("SELECT NAME FROM MY_TABLE WHERE SOME_CONDITIONS").uniqueResult();
}
}.doInHibernate(session);
}
A better way would be to create a kind of execute method in another class where you have access to the session.
With that solution you can still mark your field as #Transient.
You can use
#Column(name = "{name of column}", insertable=false, updatable = false)
Do not mark the field as #Transient.
This way this property will not be inserted or updated but can be used in selects.

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.

Problem with id column and JPA

I'm trying JPA with a very simple class for the Play! framework and I'm having some problems with the id column.
My sql database has only two columns:
CREATE TABLE IF NOT EXISTS `auto` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
)
And my model is:
#Entity
#Table(name = "auto")
public class Auto extends Model{
#Column(insertable = false, updatable = false)
public int id;
public String name;
public Auto(String name){
this.name = name;
}
}
Everything works fine without this part:
#Column(insertable = false, updatable = false)
public int id;
As soon as I add public int id; I'd get this error though: A JPA error occurred (Unable to build EntityManagerFactory): Repeated column in mapping for entity: models.Auto column: id (should be mapped with insert="false" update="false")
And that's the reason I've added the column annotation, but it doesn't work with that neither, now I'm getting:
A javax.persistence.PersistenceException has been caught, org.hibernate.PropertyAccessException: could not set a field value by reflection setter of models.Auto.id
I'm testing the model this way: new Auto("bmw").save(); save() is a method from the model class in the playframework.
Anyone know why I'm having this problem? Thanks!
Hmm, try it completely without the id field. Looks like Playframework auto-creates an Id field if extending the Model class. See here:
"...
If you have used JPA before, you know that every JPA entity must provide an #Id property. Here the Model superclass provides an automatically generated numeric ID, and in most cases this is good enough.
..."
class Model already adds an id field, of type Long. This is conflicting with the id field you add on your class definition.
Just remove the id field from Auto and it should work. I'm not sure if the definition of int(11) in your database is correct, but JPA should automatically solve that if required.
Should't the column be annotated similar to following ?
#Id
#GeneratedValue(generator="???_seq",strategy=GenerationType.SEQUENCE)
The problem you are having relates to underestimating what you are getting for ´free´ in the play framework.
Both the ID (your original question) and the getters and setters (your follow up comment) are generated automatically for you.
The id field comes from the Model class, which you are extending, and the getters and setters are automatically generated and used when you make a public field in your model, and then refer to it later as model.field.
While all models will have an Id provided for them, it is recommended to use your own custom IDs if they are to do anything more complex or meaningful.
If you need to add your own id field (for example, because it needs to be an Integer rather than a Long), you can extend GenericModel rather than Model.

Categories