Hibernate - AttributeOverride default value when using Embedded - java

How to override #Column columnDefinition in case of using #Embedded in Hibernate?
To be more specific there is an example:
#Embedded
#AttributeOverrides({ #AttributeOverride(name = "isnettogross", column = #Column(name = "isnettogross", columnDefinition="char(1) default 1", nullable = false))})
public ParentGrossNetTransformVariables grossNetTransform;
and class ParentGrossNetTransformVariables:
#Embeddable
public class ParentGrossNetTransformVariables {
#Column(name = "isnettogross", columnDefinition="char(1) default 0", nullable = false)
public boolean isNet2GrossTransform;
as i use:
lConf.setProperty("hibernate.hbm2ddl.auto", "update");
the changes in database are applied automaticly. Now I need to add ParentGrossNetTransformVariables in multiple entities but with different default value of "isnettogross" so that I try to use #AttributeOverrides but it doesnt work. Is there any way to do it ?

I am assuming "it doesn't work" as the changes made to column names not being reflected in the database.
hbm2ddl.auto=update does not update the schema. So changes you make to the column names does not get reflected in the database.
Change it to create so that it re-creates the schema based on the changes made, if any.
UPDATE:
Use insertable=false attribute along with the columnDefintion in your embeddable class, i.e.
#Column(name = "isnettogross", columnDefinition="char(1) default 0", nullable = false, insertable=false)
and the entity class,
#AttributeOverrides({ #AttributeOverride(name = "isnettogross", column = #Column(name = "isnettogross", columnDefinition="char(1) default 1", nullable = false, insertable=false))})
This ignores the column of interest in the INSERT statement that is fired, thereby inserting the default value configured.
The same goes for updatable, so you might wanna use that too based on your use case.
LATEST:
I'm quite puzzled as to how it isn't working in your code. I have tried and tested; and it's working fine.
I have attached the code too just in case you wanna have a look and cross verify.
#Entity
public class Demo {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#Embedded
#AttributeOverride(name="flag", column=#Column(name="MainFlag", columnDefinition="char(1) default 1", insertable=false))
private Misc misc;
private Misc misc2;
// Getters and setters
#Embeddable
public class Misc {
#Column(name="flag", columnDefinition="char(1) default 0", insertable=false)
private boolean flag;
// Getters and setters
The code to test is a simple session.save() which generated the query
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into Demo (id) values (?)
Attaching the values persisted in the database as well :D

Related

Representing #EmbeddedId as SQL for H2 database

I am currently working on a Java project with Hibernate entities (more below). In order to test my data access object layers, I am using H2 database to populate an in-memory database and throwing queries at it. Until this point, everything is fine.
However, the problem comes when simulating the #EmbeddedId annotation.
#Entity
#Table(name = "BSCOBJ")
public class BasicObject extends AbstractDomainObject {
#EmbeddedId // This annotation here
private RestrainPK restrain;
#Embeddable
public static class RestrainPK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "CODI", nullable = false)
private String coDi;
#Column(name = "COGA", nullable = false)
private String coGa;
#Column(name = "TYOR", nullable = false)
private String tyOr;
public RestrainPK() {
}
... // Getters and setters
}
}
"Simply" creating the table BSCOBJ and populating it gives no value when fetching data (of course, I checked that the request would give result "normally"). How do I represent this nested class in a SQL table creation / value insertion request ? Is that even possible ?
Thanks in advance,
EDIT
As requested, here is some samples about the SQL / Hibernate ran.
Creation request:
CREATE TABLE BSCOBJ (CODI VARCHAR(5) NOT NULL, COGA VARCHAR(5) NOT NULL, TYOR VARCHAR(5) NOT NULL);
Insertion request:
INSERT INTO BSCOBJ (CODI, COGA, TYOR) VALUES
('HELLO', 'MAT', 'REF'),
('BONJ', 'SOME', 'DAIL'),
('SOPA', 'KDA', 'RATIO');
Request given by Hibernate when trying to run the test code:
select r.restrain.tyOr from mypackage.BasicObject r where r.restrain.coDi = :coDi and r.restrain.coGa = :coGa
With the following values:
coDi = "BONJ";
coGa = "SOME";
Throws a NoResultException. I am expecting DAIL, from the second line of the INSERT request.
I have used #EmbeddedId only one time, but I think that you need #AttributeOverrides under your #EmbeddedId
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "idpk", column = #Column(name="IDPK", nullable = false),
#AttributeOverride(name = "code", column = #Column(name="CODE")
})
and remove your #Column annotations from FormulePK

Set default values using hibernate annotations in MYSQL

I have searched on internet and seen ideas about setting default values in entity class using hibernate annotations in mysql and I have done with setting default values to column with datatype integer only as follows.
#Column(name = "COLUMN_NAME", insertable=false, updatable = false, nullable = false, columnDefinition = "int default 1")
protected Integer test_id;
and is working fine.
But I haven't seen any example for varchar/string datatype and I have try with different types in columnDefinition like,
columnDefinition = "TEXT default `TEST`"
columnDefinition = "TEXT default TEST"
columnDefinition = "varchar(255) default `15-JUL-1980`")
columnDefinition = "varchar(255) default 15-JUL-1980")
columnDefinition = "varchar default 15-JUL-1980")
columnDefinition = "varchar default `15-JUL-1980`")
columnDefinition = "CHAR(100) default `15JUL1980`")
columnDefinition = "CHAR default `15JUL1980`")
With length attribute:-
`length=100, columnDefinition = "CHAR default `15JUL1980`")`
etc.
but in this case table is not created by hibernate, that may be due to wrong syntax.
But if I am creating the table with only integer values as above table is created.
Below line of code is working fine
#Column(name = "cname", insertable=false, updatable = false, nullable = false, columnDefinition = "varchar(255) default '15-JUL-1980'")
columnDefinition is works, but it is database dependent.
and there are many other solution(Using #PrePersist annotation, modifying 'getters'), but I recommend you set default value in DBMS, then use #DynamicInsert/#DynamicUpdate annotations in model class.
See example below(Spring data JPA + Hibernate).
// Model class
#Entity
public class Person {
private Integer age;
private String name;
private String phone;
private String address;
// setters and getters
}
// Insert method in service class
#Override
#Transactional
public Person create() {
Person createdPerson = new Person();
createdPerson.setAge(20);
createdPerson.setName("Foo");
//createdPerson.setPhone("Unknown");
//createdPerson.setAddress("Somewhere");
return personRepository.save(createdPerson);
}
Hibernate will generate following insert SQL statement.
INSERT INTO person (age, name, phone, address)
VALUES (?, ?, ?, ?)
you can see Hibernate insert unnecessary columns(phone, address), too.
but, after add #DynamicInsert annotation to Model class like below,
#Entity
#DynamicInsert
public class Person {
...
}
you can see Hibernate insert only necessary columns.
INSERT INTO person (age, name)
VALUES (?, ?)
and uninitilized columns(phone, address) set their value by DBMS default value.
simply set default value in your Java code, just initialize your variable like this - private int myColumn = 100; and String as
#Column(name="order_status" , length=10 )
private String orderStatus = "Pending";
it's work for me !! i know it's too late but i hope this will help someone !!

Hibernate: getting too many rows

I have problem with getting rows from my database using Hibernate. When I would like to get only one row, I am receiving 20. When I would like to get all of rows from table with about 1.5k rows, I am receiving exactly 15.2k rows.
Entity class of this table has composite primary key.
This is my code for getting all rows:
Criteria criteria = getSession().createCriteria(type);
criteria.setCacheable(true).setCacheRegion(BaseEntity.PACKAGE);
criteria.list();
And this is my Entity class:
#javax.persistence.Entity
#Table(name = "my_table")
public class My extends MyEntity<MyPK> {
#EmbeddedId
private MyPK id;
#Column(name = "text", nullable = false)
protected String text;
#ManyToOne
#JoinColumn(name = "property", nullable = false, insertable = false, updatable = false)
protected Option option;
#Override
public MyPK getId() {
return id;
}
#Override
public void setId(MyPK id) {
this.id = id;
}
//getters and setter
}
And this is MyPK class:
#Embeddable
public class MyPK implements Serializable {
#Column(name = "qwerty")
protected String qwerty;
#Column(name = "property")
protected String property;
//constructors, getters and setters
}
MyEntity class is abstract class with #MappedSuperclass annotation. This is this class header:
#MappedSuperclass
public abstract class MyEntity<T extends Serializable>
What am I doing wrong? Is this problem with EmbeddedId?
EDIT #1
As I have realized this is problem with this:
#ManyToOne
#JoinColumn(name = "property", nullable = false, insertable = false, updatable = false)
protected Option option;
This object contains foreign key to another table. And this another table has reference to another. And this last table has 10 rows for previous table. In the result I am getting rows amount * 10. The problem is probably with Hibernate annotation in my entities.
It looks like you're probably eagerly joining a many-to-one relationship somewhere. The default behavior is that you get one entity for each row returned by the database. If you don't want to change the eager fetching, but do want to remove duplicates in your result, you need to use this ResultTransformer:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
#Embeddable only means that MyPK 's columns will be columns in the My class.
Your problem might be #ManyToOne #JoinColumn(name = "property") since it's the same with "property" in MyPK
You can set the maximum number of results by setting this on the criteria with this method: setMaxResults(int maxResults)
Primary key classes need to define equals() and hashCode(), in terms of the aggregated values (qwerty and property, here). Most likely when process the ResultSet, Hibernate is not seeing the entity keys across multiple rows as equal.
From Section 2.4 of the JPA 2.0 specification (in case it helps):
The primary key class must define equals and hashCode methods. The semantics of value
equality for these methods must be consistent with the database equality for the database types
to which the key is mapped

Hibernation annotations, specify column default value

I have a domain object and annotated as follows
#Entity
#Table(name = "REQUEST")
public class Request {
/**
* Unique id for this request
*/
#Id
#GeneratedValue
#Column(name = "EQ_ID")
private long requestId;
/**
*
*/
#Column(name = "EMAIL_ID")
private String emailId;
/**
*
*/
#Column(name = "REQUEST_DATE")
private Date requestDate;
/**
*Getters/setters omitted
*/
}
The column Request_date cannot be null and as per the DDL the default value is sysdate (oracle DB). How do I annotate this field so that if the requestDate property is null,hiberanate automatically inserts sysdate.? Currently it throws error when the field is null,which is very obvious as it cannot be null as per the DB constraints. How do I go about this?
One alternative is to mark this field as transient and the inserts work fine. But the negative aspect is that, I will not be able to retrieve the value (of request_date column).
This is a missing feature in hibernate annotations. Also there exist some workaround as Yok has posted. The problem is that the workaround is vendor dependent and might not work for all DB. In my case,Oracle, it isn't working and has been reported as a bug.
You can put the default value in a columnDefinition. An example would look like:
#Column(name = "REQUEST_DATE", nullable = false, columnDefinition = "date default sysdate")
Using #ColumnDefault (Work for DDL update).
hibernate.hbm2ddl.auto=update
import org.hibernate.annotations.ColumnDefault;
....
#ColumnDefault(value="'#'")
#Column(name = "TEMP_COLUMN", nullable = false)
public String getTempColumn() {
return tempColumn;
}
DDL Generate:
Alter Table YOUR_TABLE add TEMP_COLUMN varchar2(255) default '#' not null;
Assign a default value to the field:
private Date requestDate = new Date();
If you mark your entity with #DynamicInsert e.g.
#Entity
#DynamicInsert
#Table(name = "TABLE_NAME")
public class ClassName implements Serializable {
Hibernate will generate SQL without null values. Then the database will insert its own default value. This does have performance implications See Dynamic Insert.
Make the default in Oracle for the column SYSDATE:
ALTER TABLE APP MODIFY (REQUEST_DATE DEFAULT SYSDATE);
Then, from Hibernate's perspective it can be nullable.
Hibernate will save a NULL to the database. Oracle will convert that to SYSDATE. And everyone will be happy.
I resolved assigning a value to the variable like this private Integer active= 0;
#Entity
#Table(name="products")
public class ServiziTipologia {
#Id
#GeneratedValue
private Integer id;
private String product;
private String description;
private Integer active= 0;

How to set a default entity property value with Hibernate

How do I set a default value in Hibernate field?
If you want a real database default value, use columnDefinition:
#Column(name = "myColumn", nullable = false, columnDefinition = "int default 100")
Notice that the string in columnDefinition is database dependent. Also if you choose this option, you have to use dynamic-insert, so Hibernate doesn't include columns with null values on insert. Otherwise talking about default is irrelevant.
But if you don't want database default value, but simply a default value in your Java code, just initialize your variable like that - private Integer myColumn = 100;
Use hibernate annotation.
#ColumnDefault("-1")
private Long clientId;
Recreate the table if it already exists for the changes to take effect.
You can use #PrePersist anotation and set the default value in pre-persist stage.
Something like that:
//... some code
private String myProperty;
//... some code
#PrePersist
public void prePersist() {
if(myProperty == null) //We set default value in case if the value is not set yet.
myProperty = "Default value";
}
// property methods
#Column(nullable = false) //restricting Null value on database level.
public String getMyProperty() {
return myProperty;
}
public void setMyProperty(String myProperty) {
this.myProperty= myProperty;
}
This method is not depend on database type/version underneath the Hibernate. Default value is set before persisting the mapping object.
what about just setting a default value for the field?
private String _foo = "default";
//property here
public String Foo
if they pass a value, then it will be overwritten, otherwise, you have a default.
Default entity property value
If you want to set a default entity property value, then you can initialize the entity field using the default value.
For instance, you can set the default createdOn entity attribute to the current time, like this:
#Column(
name = "created_on"
)
private LocalDateTime createdOn = LocalDateTime.now();
Default column value using JPA
If you are generating the DDL schema with JPA and Hibernate, although this is not recommended, you can use the columnDefinition attribute of the JPA #Column annotation, like this:
#Column(
name = "created_on",
columnDefinition = "DATETIME(6) DEFAULT CURRENT_TIMESTAMP"
)
#Generated(GenerationTime.INSERT)
private LocalDateTime createdOn;
The #Generated annotation is needed because we want to instruct Hibernate to reload the entity after the Persistence Context is flushed, otherwise, the database-generated value will not be synchronized with the in-memory entity state.
Instead of using the columnDefinition, you are better off using a tool like Flyway and use DDL incremental migration scripts. That way, you will set the DEFAULT SQL clause in a script, rather than in a JPA annotation.
Default column value using Hibernate
If you are using JPA with Hibernate, then you can also use the #ColumnDefault annotation, like this:
#Column(name = "created_on")
#ColumnDefault(value="CURRENT_TIMESTAMP")
#Generated(GenerationTime.INSERT)
private LocalDateTime createdOn;
Default Date/Time column value using Hibernate
If you are using JPA with Hibernate and want to set the creation timestamp, then you can use the #CreationTimestamp annotation, like this:
#Column(name = "created_on")
#CreationTimestamp
private LocalDateTime createdOn;
If you want to do it in database:
Set the default value in database (sql server sample):
ALTER TABLE [TABLE_NAME] ADD CONSTRAINT [CONSTRAINT_NAME] DEFAULT (newid()) FOR [COLUMN_NAME]
Mapping hibernate file:
<hibernate-mapping ....
...
<property name="fieldName" column="columnName" type="Guid" access="field" not-null="false" insert="false" update="false" />
...
See, the key is insert="false" update="false"
One solution is to have your getter check to see if whatever value you are working with is null (or whatever its non-initialized state would be) and if it's equal to that, just return your default value:
public String getStringValue(){
return (this.stringValue == null) ? "Default" : stringValue;
}
Use #ColumnDefault() annotation. This is hibernate only though.
I searched for this and found many answers to default value for column.If you want to use default value defined in SQL Table then in #Column Annotation use "insertable = false". insertable
#Column(name = columnName, length = lengthOfColumn, insertable = false)
If you are using columnDefination it #Column annotation may be it won't work as it is Database dependent.
Working with Oracle, I was trying to insert a default value for an Enum
I found the following to work the best.
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private EnumType myProperty = EnumType.DEFAULT_VALUE;
To use default value from any column of table. then you must need to define #DynamicInsert as true or else you just define #DynamicInsert. Because hibernate takes by default as a true.
Consider as the given example:
#AllArgsConstructor
#Table(name = "core_contact")
#DynamicInsert
public class Contact implements Serializable {
#Column(name = "status", columnDefinition = "int default 100")
private Long status;
}
You can use the java class constructor to set the default values. For example:
public class Entity implements Serializable{
private Double field1
private Integer field2;
private T fieldN;
public Entity(){
this.field1=0.0;
this.field2=0;
...
this.fieldN= <your default value>
}
//Setters and Getters
...
}
I tried it. when i did that
#Column(name = "is_sale", columnDefinition = "default false")
private boolean isSale = false;
he did not add. And when I did
#Column(name = "is_sale", columnDefinition = "bool default false")
private boolean isSale = false;
in this case Hibernate generated such sql
alter table if exists customer_product add column is_sale bool default false
and it helped me
<property name="age" type="integer">
<column name="age" not-null="false" default="null" />
</property>
i'am working with hibernate 5 and postgres, and this worked form me.
#Column(name = "ACCOUNT_TYPE", ***nullable***=false, columnDefinition="varchar2 default 'END_USER'")
#Enumerated(EnumType.STRING)
private AccountType accountType;
If you want to set default value in terms of database, just set #Column( columnDefinition = "int default 1")
But if what you intend is to set a default value in your java app you can set it on your class attribute like this: private Integer attribute = 1;
Suppose we have an entity which contains a sub-entity.
Using insertable = false, updatable = false on the entity prevents the entity from creating new sub-entities and preceding the default DBMS value. But the problem with this is that we are obliged to always use the default value or if we need the entity to contain another sub-entity that is not the default, we must try to change these annotations at runtime to insertable = true, updatable = true, so it doesn't seem like a good path.
Inside the sub-entity if it makes more sense to use in all the columns insertable = false, updatable = false so that no more sub-entities are created regardless of the method we use (with #DynamicInsert it would not be necessary)
Inserting a default value can be done in various ways such as Default entity property value using constructor or setter. Other ways like using JPA with columnDefinition have the drawback that they insert a null by default and the default value of the DBMS does not precede.
Insert default value using DBMS and optional using Hibernate
But using #DynamicInsert we avoid sending a null to the db when we want to insert a sub-entity with its default value, and in turn we allow sub-entities with values other than the default to be inserted.
For inserting, should this entity use dynamic sql generation where only non-null columns get referenced in the prepared sql statement?
Given the following needs:
The entity does not have the responsibility of creating new sub-entities.
When inserting an entity, the sub-entity is the one that was defined as default in the DBMS.
Possibility of creating an entity with a sub-entity which has a UUID other than the default.
DBMS: PostgreSQL | Language: Kotlin
#Entity
#Table(name = "entity")
#DynamicInsert
data class EntityTest(
#Id #GeneratedValue #Column(name = "entity_uuid") val entityUUID: UUID? = null,
#OneToOne(cascade = [CascadeType.ALL])
#JoinColumn(name = "subentity_uuid", referencedColumnName = "subentity_uuid")
var subentityTest: SubentityTest? = null
) {}
#Entity
#Table(name = "subentity")
data class SubentityTest(
#Id #GeneratedValue #Column(name = "subentity_uuid", insertable = false, updatable = false) var subentityUUID: UUID? = null,
#Column(insertable = false, updatable = false) var name: String,
) {
constructor() : this(name = "")
}
And the value is set by default in the database:
alter table entity alter column subentity_uuid set default 'd87ee95b-06f1-52ab-83ed-5d882ae400e6'::uuid;
GL
Source 1
Source 2
we can have getter that annotates #Column
all #column should be annotated in getter alone instead of direct variable declaration. by this way, we can resolve it.
#Column(name = "string_value")
public String getStringValue(){
return (this.stringValue == null) ? "Default" : stringValue;
}

Categories