Am i right that #Id annotation add two constraints in database:
nullable=false
unique=true
?
I saw a lot of examples in the Internet with syntax like
#Id
#Column(name="xxx",nullable=false)
BigInteger id
It is correct? Do i really need this nullable=false?
Yes you are right. If you use hibernate schema generation mechanism, all #Id columns in the database will be NOT NULL and have unique index by default.
In the other hand, #Column(nullable=false) declaration is absolutely meaningless if you create the schema any other way.
One reason you might see the two together is for the name attribute on #Column. It's name attribute lets you explicitly choose the name of the column in the resulting table, in cases where the default name that JPA provides. I will at times use #Column solely for that purpose, just so I can give my column a certain name.
As for the nullable attribute, I agree with you. It's worthless in that case.
I have MySql database with several tables. Every field in each table is not nullable. That's why I am forced to use #NotNull annotation to each field in all my Java classes marked with #Entity. Do I really have to do this or is there a way to tell JPA/Hibernate to treat every field NotNullable on default?
edit:
I am aware of #Column(name="something", nullable=false) too. But still, it doesn't solve any problem - you have to write nullable=false
There are no such possibility. Not Nullable constraint is not what you always expect from a field, although it is used quite often. It is convenient when you can look at the attribute definition and tell everything out of it, without addressing to some high-level settings like "every field should be #NotNull".
It would be rather confusing to see such entity definition with this setting hidden elsewhere.
And, one more thing. #NotNull annotation and #Column(name="something", nullable=false) are not the same. More details here :
Confusion: #NotNull vs #Column(nullable = false)
From #Column annotation documentation i verified nullable attribute has true as default value.
In my entities definitions I'd like to set columns non-nullable as default behavior, but I don't want to set this for every single column.
Is there a way to globally change default value for nullable attribute (and eventually others)?
JPA takes the nullable property from the tables on your database if the columns on your table were notnull then your entity attributes would be #NotNull too. You could do the changes on your database and recreate the entities.
I'm not familiar with a way to override JPA default settings, but you could use EntityListeners to perform some #PrePersist validations and check that an object fields (non transient ones) are not null by reflection.
Having said that, I believe that this makes the entity definition less clear and would rather stick to the more declarative technique using (nullable="false").
I'm using JPA 1, Hibernate and Oracle 10.2.0 and my entities are defined like this:
#Entity
#Table(name="TERMS")
public class Term implements Serializable {
#Id
#GenericGenerator(name = "generator", strategy = "guid", parameters = {})
#GeneratedValue(generator = "generator")
#Column(name="TERM_ID")
private String termId;
}
I have a situation where an XML representation of the Entity (and child entities) will be coming in through a web service to update/replace existing ones. My thought was to just delete the old ones and re-create it from the incoming XML.
However, doing a persist when my entities having existing IDs seem to make Hibernate very angry. So is this actually possible or is it better to avoid deleting them and just trying to do it with merge?
Angriness from hibernate:
org.hibernate.PersistentObjectException: detached entity passed to persist: com.idbs.omics.catalog.entity.Term
Thanks
My thought was to just delete the old ones and re-create it from the incoming XML. However, doing a persist when my entities having existing IDs seem to make Hibernate very angry..
Indeed, you cannot assign an Id when it is supposed to be generated, at least not with Hibernate that won't consider the entity as new but as detached (the JPA specification is a bit blurry on the exact rules in this case but that's how Hibernate behaves, see 5.1.4.5. Assigned identifiers for more hints).
So is this actually possible or is it better to avoid deleting them and just trying to do it with merge?
To make the delete/insert possible for the web service use case, you'd have to either:
not assign the id ~or~
use a special version of the entity without a generated identifier ~or~
use bulk operations(?)
The alternative if you're actually updating detached entities would be indeed to use a merge (but have a look at these previous questions just in case).
Which approach is better? I don't know, it think it depends on your needs. The later seems more natural if you're updating existing entities. With the former, you'd really get "new" entities (including a new value for the optimistic locking column). Depending on the exact implementation of the process, performances might also vary. And, by the way, what about concurrency (just to mention it, I'm not really expecting an answer)?
You can use EntityManager.merge to save an updated version of the entity. Be aware that this returns another object than the one you pass to it, because it basically fetches the entity from the database, updates the persistent properties from the object you pass and saves the persistent object.
See http://blog.xebia.com/2009/03/23/jpa-implementation-patterns-saving-detached-entities/ for more information on this problem.
I have a PostgreSQL table in which I have a column inv_seq declared as serial.
I have a Hibernate bean class to map the table. All the other columns are read properly except this column. Here is the declaration in the Hibernate bean class:
....
....
#GeneratedValue(strategy=javax.persistence.GenerationType.AUTO)
#Column(name = "inv_seq")
public Integer getInvoiceSeq() {
return invoiceSeq;
}
public void setInvoiceSeq(Integer invoiceSeq) {
this.invoiceSeq = invoiceSeq;
}
....
....
Is the declaration correct?
I am able to see the sequential numbers generated by the column in the database, but I am not able to access them in the java class.
Please help.
Danger: Your question implies that you may be making a design mistake - you are trying to use a database sequence for a "business" value that is presented to users, in this case invoice numbers.
Don't use a sequence if you need to anything more than test the value for equality. It has no order. It has no "distance" from another value. It's just equal, or not equal.
Rollback:
Sequences are not generally appropriate for such uses because changes to sequences are't rolled back with transaction ROLLBACK. See the footers on functions-sequence and CREATE SEQUENCE.
Rollbacks are expected and normal. They occur due to:
deadlocks caused by conflicting update order or other locks between two transactions;
optimistic locking rollbacks in Hibernate;
transient client errors;
server maintenance by the DBA;
serialization conflicts in SERIALIZABLE or snapshot isolation transactions
... and more.
Your application will have "holes" in the invoice numbering where those rollbacks occur. Additionally, there is no ordering guarantee, so it's entirely possible that a transaction with a later sequence number will commit earlier (sometimes much earlier) than one with a later number.
Chunking:
It's also normal for some applications, including Hibernate, to grab more than one value from a sequence at a time and hand them out to transactions internally. That's permissible because you are not supposed to expect sequence-generated values to have any meaningful order or be comparable in any way except for equality. For invoice numbering, you want ordering too, so you won't be at all happy if Hibernate grabs values 5900-5999 and starts handing them out from 5999 counting down or alternately up-then-down, so your invoice numbers go: n, n+1, n+49, n+2, n+48, ... n+50, n+99, n+51, n+98, [n+52 lost to rollback], n+97, .... Yes, the high-then-low allocator exists in Hibernate.
It doesn't help that unless you define individual #SequenceGenerators in your mappings, Hibernate likes to share a single sequence for every generated ID, too. Ugly.
Correct use:
A sequence is only appropriate if you only require the numbering to be unique. If you also need it to be monotonic and ordinal, you should think about using an ordinary table with a counter field via UPDATE ... RETURNING or SELECT ... FOR UPDATE ("pessimistic locking" in Hibernate) or via Hibernate optimistic locking. That way you can guarantee gapless increments without holes or out-of-order entries.
What to do instead:
Create a table just for a counter. Have a single row in it, and update it as you read it. That'll lock it, preventing other transactions from getting an ID until yours commits.
Because it forces all your transactions to operate serially, try to keep transactions that generate invoice IDs short and avoid doing more work in them than you need to.
CREATE TABLE invoice_number (
last_invoice_number integer primary key
);
-- PostgreSQL specific hack you can use to make
-- really sure only one row ever exists
CREATE UNIQUE INDEX there_can_be_only_one
ON invoice_number( (1) );
-- Start the sequence so the first returned value is 1
INSERT INTO invoice_number(last_invoice_number) VALUES (0);
-- To get a number; PostgreSQL specific but cleaner.
-- Use as a native query from Hibernate.
UPDATE invoice_number
SET last_invoice_number = last_invoice_number + 1
RETURNING last_invoice_number;
Alternately, you can:
Define an entity for invoice_number, add a #Version column, and let optimistic locking take care of conflicts;
Define an entity for invoice_number and use explicit pessimistic locking in Hibernate to do a select ... for update then an update.
All these options will serialize your transactions - either by rolling back conflicts using #Version, or blocking them (locking) until the lock holder commits. Either way, gapless sequences will really slow that area of your application down, so only use gapless sequences when you have to.
#GenerationType.TABLE: It's tempting to use #GenerationType.TABLE with a #TableGenerator(initialValue=1, ...). Unfortunately, while GenerationType.TABLE lets you specify an allocation size via #TableGenerator, it doesn't provide any guarantees about ordering or rollback behaviour. See the JPA 2.0 spec, section 11.1.46, and 11.1.17. In particular "This specification does not define the exact behavior of these strategies. and footnote 102 "Portable applications should not use the GeneratedValue annotation on other persistent fields or properties [than #Id primary keys]". So it is unsafe to use #GenerationType.TABLE for numbering that you require to be gapless or numbering that isn't on a primary key property unless your JPA provider makes more guarantees than the standard.
If you're stuck with a sequence:
The poster notes that they have existing apps using the DB that use a sequence already, so they're stuck with it.
The JPA standard doesn't guarantee that you can use generated columns except on #Id, you can (a) ignore that and go ahead so long as your provider does let you, or (b) do the insert with a default value and re-read from the database. The latter is safer:
#Column(name = "inv_seq", insertable=false, updatable=false)
public Integer getInvoiceSeq() {
return invoiceSeq;
}
Because of insertable=false the provider won't try to specify a value for the column. You can now set a suitable DEFAULT in the database, like nextval('some_sequence') and it'll be honoured. You might have to re-read the entity from the database with EntityManager.refresh() after persisting it - I'm not sure if the persistence provider will do that for you and I haven't checked the spec or written a demo program.
The only downside is that it seems the column can't be made # NotNull or nullable=false, as the provider doesn't understand that the database has a default for the column. It can still be NOT NULL in the database.
If you're lucky your other apps will also use the standard approach of either omitting the sequence column from the INSERT's column list or explicitly specifying the keyword DEFAULT as the value, instead of calling nextval. It won't be hard to find that out by enabling log_statement = 'all' in postgresql.conf and searching the logs. If they do, then you can actually switch everything to gapless if you decide you need to by replacing your DEFAULT with a BEFORE INSERT ... FOR EACH ROW trigger function that sets NEW.invoice_number from the counter table.
I have found that hibernate 3.6 tries to use a single sequence for all entities when you set it to AUTO so in my application I use IDENTITY as the generation strategy.
#Id
#Column(name="Id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Craig had some very good points about invoice numbers needing to incrementing if you are presenting them to users and he suggested using a table for that. If you do end up using a table to store the next id you might be able to use a mapping similar to this one.
#Column(name="Id")
#GeneratedValue(strategy=GenerationType.TABLE,generator="user_table_generator")
#TableGenerator(
name="user_table_generator",
table="keys",
schema="primarykeys",
pkColumnName="key_name",
pkColumnValue="xxx",
valueColumnName="key_value",
initialValue=1,
allocationSize=1)
private Integer id;
Correct syntax is as follows:
#Column(name="idClass", unique=true, nullable=false, columnDefinition = "serial")
#Generated(GenerationTime.INSERT)
private Integer idClass;
Depending on your situation, this may not work. There is a bug opened against Hibernate that documents this behavior.
http://opensource.atlassian.com/projects/hibernate/browse/HHH-4159
If you are open to using a mapping file instead of annotations, I have been able to recreate the issue (NULL's in SERIAL columns that are not part of the primary key). Using the "generated" attribute of the property element causes Hibernate to re-read the row after an insert to pick up the generated column value:
<class name="Ticket" table="ticket_t">
<id name="id" column="ticket_id">
<generator class="identity"/>
</id>
<property name="customerName" column="customer_name"/>
<property name="sequence" column="sequence" generated="always"/>
</class>
I use Postgres + Hibernate in my projects and this is what I do:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "hibernate_sequence")
#SequenceGenerator(name = "hibernate_sequence", sequenceName = "hibernate_sequence")
#Column(name = "id", unique = true, nullable = false)
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
It works just fine for me.