When I create a new instance and then flush() my Hibernate Session, are all properties of this instance marked as dirty, including the null ones?
Or are only those properties dirty that aren't null?
There is no dirty values not in Java neither in DBMS (Mysql for example). By default in java all primitive types takes specified zero values and all links takes null as value. The same for DBMS, when you insert a record in table field of record initialized with default values.
Related
I'm writing a simple webapp to show my coding skills to potential employers. It connects with an API and receives a JSON file which is then deserialized using Jackson and displayed in a table form in the browser. I want to enable the user to persist the Java object in a Postgres database using Hibernate. I got it to work and it does the job nicely but I want to make it more efficient.
Whenever there is no data in the JSON response to put in the object's field (right now all the possible JSON attributes are present in the Java class/Hibernate entity in the form of String fields) I put an empty String ('') and then, with all fields having something and no null objects, it is stored in the database.
Should I only store what I have and put no empty strings in the DB (using nulls instead) or is what I'm doing now the right way?
Null is an absence of a value. An empty string is a value. But that don't impact much to memory. If you want to display data repeatedly and don't want conversion from null to empty string while retrieval you can go for empty string ''.
But if you want unique constraint for values other than empty string '' then use null.
Sometimes null and empty '' can be used to differentiate either data was known or not. for known but not available data use empty and for unknown data null can be used.
Use NULLwhen there isn't a known value.
Never use the empty string.
For example, if you have a customer which didn't supply his address don't say his address is '', say it is NULL. NULL unambiguously states "no value".
For database columns that must have a value for your web application to work, create the backing table with NOT NULL data constraints on those columns.
In your unit tests, call NULL, ..._address_is_null_ and test for success or failure (depending on if the test should trigger no errors or trigger an exception).
The use of '' in databases as a sentinel, a special value that means something other that '', is discouraged. That's because we won't know what you meant it to mean. Also, there might be more than one special case, and if you use '' first, then it makes restructuring more difficult to add others (unless you fall into the really bad practice of using even more special strings to enumerate other special cases, like "deleted" and so on).
It seems that JOOQ is completely ignoring the default values of database columns. Neither gets the ActiveRecord object updated nor does it skip this column on INSERT. Instead it tries to set it to NULL which fails on NOT NULL columns.
Example:
CREATE TABLE bug (
foo int,
bar int not null default 42
);
BugRecord b = jooq.newRecord(BUG);
b.setFoo(3);
b.store();
assertNotNull(b.getBar()); // fails
Record r = jooq.select().from(BUG).fetchOne();
assertEquals(new Integer(-1), r.getValue(BUG.BAR)); // fails
// DataMapper pattern
Bug b = new Bug();
b.setFoo(3);
bugDao.insert(b); // Fails because it tries to set "bar" to NULL
The behaviour I would expect is that either the newRecord() initializes all default variables with the korrekt values (although I understand that this could be difficult if the result is the outcome of a custom function :-)).or that the INSERT INTO does not insert all unmodified columns with default values and then that the INSERT INTO is followed by a SELECT that fetches the now existing values from the database (similar to a RETURNING).
Is this really a bug/limitation or am I missing some config option etc which makes it
possible to use "not null default" columns?
You've spotted a couple of things here (all relevant to jOOQ 3.1 and previous versions):
Returning default values from inserts:
BugRecord b = jooq.newRecord(BUG);
b.setFoo(3);
b.store();
assertNotNull(b.getBar()); // fails
That would be a nice-to-have feature, indeed. Currently, jOOQ only fetches IDENTITY column values. You can use the INSERT .. RETURNING syntax or the UPDATE .. RETURNING syntax to explicitly chose which columns ought to be returned after an insert or update. But being able to do so in regular CRUD operations would be much better.
This had also been mentioned in this thread. The relevant feature request for this is #1859.
You can work around this issue by calling
b.refresh(); // Refresh all columns
b.refresh(BUG.BAR, ...); // Refresh only some columns
Inserting NULL vs. inserting DEFAULTs through UpdatableRecord:
Record r = jooq.select().from(BUG).fetchOne();
assertEquals(new Integer(-1), r.getValue(BUG.BAR)); // fails
This is a bug, in my opinion. jOOQ's CRUD operations should be DEFAULT value safe. Only those values that have been set explicitly prior to a store() / insert() / update() operation ought to be rendered in the generated SQL. I have registered #2698 for this.
Inserting NULL vs. inserting DEFAULTs through DAO:
// DataMapper pattern
Bug b = new Bug();
b.setFoo(3);
bugDao.insert(b); // Fails because it tries to set "bar" to NULL
Nice catch. This is non-trivial to solve / enhance, as a POJO does not ship with an internal "changed" / "dirty" flag per column. It is thus not possible to know the meaning of a null reference in a POJO.
On the other hand, jOOQ already knows whether a column is nullable. If jOOQ also maintained metadata about the presence of a DEFAULT clause on a column, it could deduce that the combination NOT NULL DEFAULT would have to lead to:
INSERT INTO bug(foo, bar)
VALUES(3, DEFAULT)
And to
UPDATE bug SET bar = DEFAULT WHERE foo = 3
I have registered
#2699: Adding some metadata information to generated code
#2700: Leveraging the above metadata in SQL from DAOs
Some of the non-nullable fields in table have default value. When inserting new rows into table via JPA, I do not want to pass any value for these fields so that they get the default values. However, when inserting new row via Spring JPA repository classes, I get an error that null values cannot be inserted. I noticed that the insert statement JPA sends to the database have all fields listed:
insert into table (field1, field2, field3) values ('abc',null,null);
Since field2 and field3 have null specified, the default values are not assigned and database throws error that null values cannot be inserted. Is there a workaround?
You can try to configure insertable property for #Column, which shouldn't be persisted & to exclude it from the insert statement.
From Documentation - insertable : (Optional) Whether the column is included in SQL INSERT statements generated by the persistence provider.
If you want to assign default value to database. you shouldn't insert it as NULL rather you have to leave it out, and don't insert it, to do that you can use, #Column(insertable = false)
annotation.
As the matter of fact I think it's not good job to assign default value in database when you work to gather with ORM. choose an other way such as JPA Events to initiate all values in JAVA.
Does anyone know how Hibernate knows whether to INSERT or to UPDATE a value in the database when session.saveOrUpdate() is called?
So far, I have only determined that it is not dependent on the information in the cache, and that the existence of the entity in the database is determined by the primary key.
When you use .saveOrUpdate() Hibernate will check if the object is transient (it has no identifier property) and if so it will make it persistent by generating it the identifier and assigning it to session. If the object has an identifier already it will perform .update().
From the documentation:
saveOrUpdate() does the following:
if the object is already persistent
in this session, do nothing
if another object associated with the
session has the same identifier,
throw an exception
if the object has no identifier
property, save() it
if the object's identifier has the
value assigned to a newly
instantiated object, save() it
if the object is versioned by a
"version" or "timestamp", and the
version property value is the same
value assigned to a newly
instantiated object, save() it
otherwise update() the object
Perhaps it is helpful to quote the Hibernate bible (Java Persistence with Hibernate, 2nd ed., page 528):
More experienced Hibernate users use saveOrUpdate() exclusively; it's much easier to let Hibernate decide what is new and what is old, especially in a more complex network of objects with mixed state. The only (not really serious) disadvantage of exclusive saveOrUpdate() is that it sometimes can't guess whether an instance is old or new without firing a SELECT at the database - for example, when a class is mapped with a natural composite key and no version or timestamp property.
How does Hibernate detect which instances are old and which are new? A range of options is available. Hibernate assumes that an instance is an unsaved transient instance if:
The identifier property is null.
The version or timestamp property (if it exists) is null.
A new instance of the same persistent class, created by Hibernate internally, has the same database identifier values as the given instance.
You supply an unsaved-value in the mapping document for the class, and the value of the identifier property matches. The unsaved-value attribute is also available for version and timestamp mapping elements.
Entity data with the same identifier value isn't in the second-level cache.
You supply an implementation or org.hibernate.Interceptor and return Boolean.TRUE from Interceptor.isUnsaved() after checking the instance in your code.
As stated here, saveOrUpdate either saves a transient instance by generating a new identifier or updates/reattaches the detached instances associated with its current identifier. More specifically it does:
if the object is already persistent in this session, do nothing
if another object associated with the session has the same identifier, throw an exception
if the object has no identifier property, save() it
if the object's identifier has the value assigned to a newly instantiated object, save() it
if the object is versioned by a <version> or <timestamp>, and the version property value is
the same value assigned to a newly instantiated object, save() it
otherwise update() the object
This is done based on the value of the primary key. If the primary key is undefined, it's value will default to 0 for numeric surrogate keys and save will be performed. If the primary key is filled out, it will invoke an update.
If someone not really understood in theory then there is a code
MyModel sent = myDao.myDaoImpl(id);
if(sent == null){
sent = **new MyModel();** // new Object
sent.setXX(id);
sent.setYY("Yes");
sent.setDate(new Date());
myDao.saveOrUpdate(sent); // Insert will be called
} else if(! "Yes".equalsIgnoreCase(sent.getFlag())) {
sent.setXX("Yes");
sent.setDate(new Date());
myDao.saveOrUpdate(sent); // Update will be called
}
What is the uses of setNull() method in PreparedStatement interface? I looked in this post.
It says: Without the setNull(..) method there would be no way to set null values for the Java primitives.
however with autoboxing in JDK5, I think null values can be set on even primitive types.
There is another post in some other forum says:If you want to be portable to different databases, use the setNull() method.
However there is nothing clearly mentioned in Java doc. Could you help me understanding this?
I think it's easier to understand this if you view it from the database end. If you want to set a field to NULL in your database insert statement, then you need a way of telling the database that is should be set to NULL rather than the default value for the column. If in the database schema you have a nullable integer field, you would use set null to set it to the DB NULL value, rather than to its default value ( 0 ).