I have a boolean property in my entity. Here's my annotations for it:
#Column(name = "IS_ACTIVE", nullable = false, columnDefinition="BIT DEFAULT 1", length = 1)
public Boolean getActive() {
return isActive;
}
But columnDefinition="BIT DEFAULT 1" doen't work perfectly. Here's SQL code I get as result for generated table:
IS_ACTIVE BIT(1) NOT NULL,
What am I doing wrong?
And so when I try to save an instance of this class to the database I get the exception:
`com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'IS_ACTIVE' cannot be null`
If I remove nullable = false property:
#Column(name = "IS_ACTIVE", columnDefinition="BIT DEFAULT 1", length = 1)
public Boolean getActive() {
return isActive;
}
so I can save a created object in this case. But it's still the default value is not set and I get NULL in the value of this field in database.
Any ideas please? I use MySQL Server 5.1 if it's important. I would be very grateful for any help. Thanks in advance!
Try using BOOLEAN data type, define your #Column annotation like that:
#Column(name = "IS_ACTIVE", columnDefinition = "boolean default true", nullable = false)
private Boolean active = true;
reference to the correct answer in this link How to set default value in Hibernate
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.
The key of the solution is dynamic-insert annotation. You can add it to your entity class as this example: #org.hibernate.annotations.Entity(dynamicInsert = true)
Related
I have a JPA entity, which contains two column, the createdTime and modifiedTime. I'd like to use MySQL's DEFAULT CURRENT_TIMESTAMP feature to generate the proper time value for this two column, so i write my entity class like:
#Entity
public class Blog {
// ...
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_time", updatable = false, insertable = false, nullable = false,
columnDefinition = "datetime default CURRENT_TIMESTAMP")
private Date createdTime;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "modified_time", updatable = false, insertable = false, nullable = false,
columnDefinition = "datetime default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
private Date modifiedTime;
}
Then i do some experiment on my application, when i check the database, the value of this two column in the database is updated properly.
However, when i check the java entity return by BlogRepository::save, the value of this two column is not the newest value. For example, when i insert a new Blog entity, i get null for this two properties on the BlogRepository::save return value.
I've read some post like https://stackoverflow.com/a/12237110/8510613, understand that if i mark the column as updatable = false, this situation will happen. So how should i fix this? Simply remove the updatable = false/insertable = false seems have no help, but will trigger the column's not null constraint violation. But if i remove the not null constraint on this two column, the value of this two column are just simply null.
You should use #Generated annotation in this case.
2.3.18. Generated properties
Generated properties are properties that have their values generated by the database. Typically, Hibernate applications needed to refresh objects that contain any properties for which the database was generating values. Marking properties as generated, however, lets the application delegate this responsibility to Hibernate. When Hibernate issues an SQL INSERT or UPDATE for an entity that has defined generated properties, it immediately issues a select to retrieve the generated values.
So, you should correct your mapping something like this:
#Temporal(TemporalType.TIMESTAMP)
#Generated(value = GenerationTime.INSERT)
#Column(name = "created_time", updatable = false, insertable = false, nullable = false,
columnDefinition = "datetime default CURRENT_TIMESTAMP")
private Date createdTime;
#Temporal(TemporalType.TIMESTAMP)
#Generated(value = GenerationTime.ALWAYS)
#Column(name = "modified_time", updatable = false, insertable = false, nullable = false,
columnDefinition = "datetime default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
private Date modifiedTime;
Let's suppose I have a primary key ts of type Timestamp. That field is generated automatically on insert into my PostgreSQL table. I want to tell JPA/Hibernate not to send or populate that field but just ignore it when the entity is being persisted.
...
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "mygenerator")
#TableGenerator(name = "mygenerator", table = "ms.time_series")
#Column(name = "ts", insertable = false, updatable = false, columnDefinition= "TIMESTAMP WITHOUT TIME ZONE")
private Timestamp ts;
...
I cannot get it to work. It looks like the #Id and #Columns(insertable = false, updatable = false) don't work together, that setting is being ignored and the framework is trying to modify or send the value in anyways.
I am using the latest Spring 5 and Spring Data 2.1.2 with Hibernate. Database is PostgreSQL.
Any idea what can be wrong? How can I set up the annotations properly for this to work?
I have one JPA entity that has a created date and a modified date column. On creation/persist, both the created date and modified date are generated by the default value given in the database, which is a timestamp. The creation works, however, when I try to do an update/merge, I cannot figure out how to change the modified date by using the default value in the database. Any advice? This is the current setup:
....
#Temporal(TemporalType.DATE)
#Column(name="CREATED_DATE", insertable = false, updatable = false)
private Date createdDate;
#Temporal(TemporalType.DATE)
#Column(name="MODIFIED_DATE", insertable = false)
private Date modifiedDate;
....
-
public Database changeDate(Database oldValues)
Database newvalues = new Database();
....
newValues.setCreatedDate(oldValues.getCreatedDate);
//newValues.setModifiedDate(); <-- (Should use the default value in the database)
....
em.merge(newValues); <-- (EntityManager)
em.getTransaction().commit();
** Just in case I didn't make myself clear, I just don't know how to make it update with the default value set in the database.
you want to set Null value to date. please add nullable property in your date annotation. it will work.
add it in your annotation nullable = true
#Temporal(TemporalType.DATE)
#Column(name="CREATED_DATE",nullable = true, insertable = false, updatable = false)
private Date createdDate;
#Temporal(TemporalType.DATE)
#Column(name="MODIFIED_DATE", nullable = true, insertable = false)
private Date modifiedDate;
There is an annotation you could use javax.persistence.Version
#Column(name="CREATED_DATE", updatable = false)
private java.sql.Timestamp createdDate = new java.sql.Timestamp(System.currentTimeMillis());
#Version
#Column(name="MODIFIED_DATE")
private java.sql.Timestamp modifiedDate;
Both fields need only have a getter, no setter. The JPA will compare and update the timestamp value on it's own. Otherwise if you rely on the database to generate it for you, you might as well try #GeneratedValue(strategy = GenerationType.IDENTITY) or AUTO, but I'm pretty sure that won't work in Hibernate. Maybe it will work in other JPA providers. In Hibernate, the AUTO is value always falls-back to SEQUENCE, so no chance of that strategy working.
If I understand the question correctly, you want to make sure that the MODIFIED_DATE column always gets set to the CURRENT_TIMESTAMP. Then, when a Database entity gets updated and it's other fields modified, the MODIFIED_DATE column will be set to the CURRENT_TIMESTAMP.
I assume that your DB table looks something like this:
CREATE TABLE Database (
ID int NOT NULL,
CREATED_DATE TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
MODIFIED_DATE TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
What you want to do is add a #PreUpdate method to your Database entity class and have it set the modifiedDate attribute to a new Date() each time. Then, whenever an update is made to the entity, the MODIFIED_DATE column will be set to a new date!
Here is an example:
#PreUpdate
protected void onUpdate() {
modifiedDate = new Date();
}
Try a simple example and see how it works:
em.getTransaction().begin();
Database db = new Database(1, "First Value");
em.persist(db);
em.getTransaction().commit();
em.getTransaction().begin();
Database db2 = em.find(Database.class, 1);
db2.setValue("New Value!");
em.getTransaction().commit();
I am using Hibernate to interface with SQL Server 2016/Azure SQL Server currently, and have been having a great time with it so far. In my database, I have implemented system versioned temporal tables. I want to map (preferably lazily) two more variables by annotation only to my Hibernate entity that represent the original ValidFrom and UpdatedBy fields from the temporal history of the appropriate table.
For example, I have a class and table for Accounts. The Account [minus nonrelated columns, constraints, etc] table is as follows:
CREATE TABLE [dbo].[Account] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[UpdatedBy] INT NOT NULL,
[ValidFrom] DATETIME2 (7) GENERATED ALWAYS AS ROW START DEFAULT (sysutcdatetime()) NOT NULL,
[ValidTo] DATETIME2 (7) GENERATED ALWAYS AS ROW END DEFAULT (CONVERT([datetime2],'9999-12-31 23:59:59.9999999')) NOT NULL,
CONSTRAINT [FK_Account.UpdatedById_Account.Id] FOREIGN KEY ([UpdatedBy]) REFERENCES [dbo].[Account] ([Id]),
PRIMARY KEY CLUSTERED ([Id] ASC),
PERIOD FOR SYSTEM_TIME ([ValidFrom], [ValidTo])
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE=[dbo].[AccountHistory], DATA_CONSISTENCY_CHECK=ON));
The SQL statement to get the data that I want looks like this (I imagine that I would select only UpdatedBy or ValidFrom per annotation, but they are together now to be concise):
SELECT UpdatedBy, ValidFrom FROM dbo.Account
FOR SYSTEM_TIME ALL
WHERE ValidFrom IN
(
SELECT MIN(ValidFrom) OVER (Partition BY Id) AS ValidFrom
FROM dbo.Account
FOR SYSTEM_TIME ALL
WHERE ID = $(passedInIdOfThisEntity)
)
Finally, my Hibernate entity/pojo looks something like this (again, redacting irrelevant variables):
#Entity
#Table(name = "Account")
public class Account implements Serializable {
#Id
#Column(name = "Id", unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "UpdatedBy")
private Account updatedBy;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "ValidFrom", nullable = false, length = 27, insertable = false, updatable = false)
private Date validFrom;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "ValidTo", nullable = false, length = 27, insertable = false, updatable = false)
private Date validTo;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "updatedBy")
private Set<Account> accountsUpdated;
// This is a stub of what I'm hoping you can help me add
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "ValidFrom", fetch = FetchType.LAZY, insertable = false, updatable = false, somesqlselect = SQL_STATEMENT_FROM_ABOVE)
private Date createdOn;
#Column(name = "UpdatedBy", fetch = FetchType.LAZY, insertable = false, updatable = false, somesqlselect = SQL_STATEMENT_FROM_ABOVE)
private Account createdBy
// ... getters and setters below
}
I have been using Hibernate to a great extent, but have had trouble finding information on this, though I have found and used examples of implementing native queries for retrieving entities instead of using criteria queries. If you can help me solve this riddle to allow me to continue using criteria queries to retrieve data and populate these fields through annotation on demand, I would greatly appreciate it.
The temporal table constructs that you've described isn't something that I am aware that JPA or even Hibernate support natively. These are likely new features of the ANSI SQL standard which haven't made their way into proper support.
That said, that doesn't mean you cannot use frameworks like Hibernate to accomplish the task. As indicated in the comments, you can specify a named query and execute that in order to get the attributes you desire.
From a JPA 2.1 perspective, you use #SqlResultSetMapping and #ConstructorResult.
#SqlResultSetMapping(
name = "Account.getWithTemporalAttributes",
classes = {
#ConstructorResult(
targetClass = com.company.domain.AccountTemporalDetails.class,
columns = {
#ColumnResult(name = "col1"),
#ColumnResult(name = "col2")
})
})
To use this, you would do the following:
Query query = entityManager.createNativeQuery(
"SELECT a.col1 as col1, a.col2 as col2 FROM Account a",
"Account.getWithTemporalAttributes");
List<AccountTemporalDetails> results = query.getResultList();
That should allow you to use Native SQL queries, mapping them to a POJO which you can easily then use within your application without having to write boilerplate. The #ConstructorResult annotation is meant to mimic the JPQL SELECT NEW syntax. So you would just need to make sure that AccountTemporalDetails had a constructor that takes those arguments with the right types.
I have a table containing customer data in an oracle database. Here is a simplified definition:
CUSTOMER (CUSTOMER_ID NUMBER NOT NULL,
SOURCE_SYSTEM VARCHAR2(30),
FULL_NAME VARCHAR2(360),
PHONE_NUMBER VARCHAR2(240)
)
The primary key for this table is (CUSTOMER_ID, SOURCE_SYSTEM).
The table has numerous rows for which SOURCE_SYSTEM is null. At the database level, there is no issue, however when I try to access any of these rows via JPA Entity, it causes a number of issues:
1: Using em.find() to fetch a row with a null SOURCE_SYSTEM always results in a null being returned.
2: Using em.merge() to upsert a row with a null SOURCE_SYSTEM succeeds if the record does not exist in the table, but fails on subsequent updates because the merge ALWAYS results in an insert being run.
3: Using em.createQuery() to explicitly query for a row with a null causes the following exception:
Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.3.1.v20111018-r10243):
org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [ArrayRecord(
CUSTOMER.CUSTOMER_ID => 1
CUSTOMER.FULL_NAME => GUY PERSON
CUSTOMER.PHONE_NUMBER => 555-555-1234
CUSTOMER.SOURCE_SYSTEM => null)] during the execution of the query was detected to be null.
Primary keys must not contain null.
Query: ReadAllQuery(referenceClass=Customer sql="SELECT CUSTOMER_ID, FULL_NAME, PHONE_NUMBER, SOURCE_SYSTEM FROM CUSTOMER WHERE ((CUSTOMER_ID = ?) AND (SOURCE_SYSTEM IS NULL))")
Unfortunately, "Primary keys must not contain null" seems pretty final. I was unable to find too much information on workarounds for this error, which makes it seem like there is no solution.
THE QUESTION: I would like to know if anyone has any Java code-based solution that don't involve making changes to the database. My current workaround is to use ROW_ID as the #Id of the table, but this means I can no longer use em.merge() or em.find().
Here are my Java classes:
Customer.java:
#Entity
#Table(name = "CUSTOMER")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private Customer_Id key;
#Column(name = "CUSTOMER_ID", nullable = false, insertable = false, updatable = false)
private Long customerId;
#Column(name = "SOURCE_SYSTEM", length = 30, insertable = false, updatable = false)
private String sourceSystem;
#Column(name = "FULL_NAME", length = 360)
private String fullName;
#Column(name = "PHONE_NUMBER", length = 240)
private String phoneNumber;
//Setters, Getters, etc
...
}
Customer_Id.java
#Embeddable
public class Customer_Id implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "CUSTOMER_ID", nullable = false)
private Long customerId;
#Column(name = "SOURCE_SYSTEM", length = 30)
private String sourceSystem;
//Setters, Getters, etc
...
}
Primary keys cannot contain null (in JPA or in databases). Use a different value such as "" or " ".
Is the customer_id unique? if so then just remove the sourceSystem from the Id.
Otherwise, you could try logging a bug to have support for null ids added.