Is there a way to make a field non-persistent at update operation but persistent at create operation with JPA - Hibernate 4?
I tried it in this way
#Transient
#Id
#Column(name = "USER_NAME", nullable = false, length = 75)
private String userName;
but with #Transient annotation the field will be transient across all CRUD operations and I want a way to specify that only on this operation is persistent (create).
Is there a way to do this?
Thanks!
You need to set updatable attribute of the #Column annotation to false:
#Column(name = "USER_NAME", nullable = false, length = 75, updatable= false)
private String userName;
The updatable attribute instruct Hibernate to omit this column from the generated UPDATE SQL statement.
I removed the #Transient and the #Id annotations.
If this column is your PK (mapped to the entity identifier), then you can only set it during INSERT, since Hibernate doesn't allow you to update an entity identifier (the updatable attribute being redundant in this case).
Related
Am new to Hibernate and MySQL, i have two Table like OFFER_TABLE and OFFER_LIKES_DISLIKES
OFFER_TABLE Columns
OFR_ID(PK)
OFR_MSG
OFFER_LIKES_DISLIKES Columns
OFFER_LIKES_DISLIKES_ID
OFR_ID(FK)
LIKE
DISLIKE
I want to map OFFER_TABLE and OFFER_LIKES_DISLIKES, Access the OFFER_LIKES_DISLIKES data through OFFER_TABLE. Am using One to One Mapping but its not Working.
Hibernate Annotation Mapping Java Class
Offers.class
#Id
#GeneratedValue
#Column(name = "OFR_ID", length = 11, nullable = false)
private int offer_id;
#OneToOne
#JoinColumn(name="OFR_ID", unique = true)
#OneToOne(cascade = CascadeType.ALL)
private MessageLikeDislikesDAO likeDislikes;
LikeDislike.class
#GeneratedValue
#Column(name="LIKES_DISLIKES_ID", length = 11, nullable = false)
private int likes_dislikes_id;
#Expose
#Column(name="OFR_ID", length = 11, nullable = false)
private int offer_id;
When I get the Data of Offers, want Like and Dislike data associated with it. In LikeDislike table OFR_ID is UNIQUE. Am used One to One. But i didn't get the data of LikeDislike. Which one is best way to took that data. Help me to solve this issue.
There are many problems:
You're creating an association with a DAO instead of creating an association with an entity
You're storing the ID of the offer in the LikeDislike entity instead of storing an association with the Offer entity
You're saying that there is a join column named OFR_ID and referring to the LikeDislike entity in the OFFER table.
You disrespect Java naming conventions
You're setting two OneToOne annotations on the same field
The mapping should be:
Offer:
#Id
#GeneratedValue
#Column(name = "OFR_ID", length = 11, nullable = false)
private Integer offerId;
#OneToOne(mappedBy = "offer", cascade = CascadeType.ALL)
private LikeDislike likeDislike;
LikeDislike:
#GeneratedValue
#Column(name="LIKES_DISLIKES_ID", length = 11, nullable = false)
private Integer likeDislikeId;
#Expose
#OneToOne
#JoinColumn(name="OFR_ID", length = 11, nullable = false)
private Offer offer;
Relevant documentation
If a field is annotated updatable=false, the column will not be included in SQL UPDATE.
This is desired default behavior. But in one specific flow, I need to update this this column. Is there a way to ignore this annotation? Or do I need to DELETE and INSERT entry?
#Column(name = "client_rating", updatable = false)
private Integer clientRating;
Toplink can use read-only mappings when multiple attributes in an object map to the same fields in the database but only one of the mappings can write to the field.
Does JPA has such feature, how to write annotation? I have one #ManyToOne and one #Column annotation which need to map to same field in database.
#ManyToOne(optional=false, fetch=FetchType.LAZY)
#JoinColumn(name="USR_ID", referencedColumnName="USER_ID", nullable=false)
private User user;
/** #generated **/
#Column(name="USER_ID", nullable=false, length=30)
private String userId;
From here
The Column annotation and XML element defines insertable and updatable options. These allow for this column, or foreign key field to be omitted from the SQL INSERT or UPDATE statement. These can be used if constraints on the table prevent insert or update operations. They can also be used if multiple attributes map to the same database column, such as with a foreign key field through a ManyToOne and Id or Basic mapping. Setting both insertable and updatable to false, effectively mark the attribute as read-only.
So
#Column(name="USER_ID", nullable=false, length=30,
updatable=false, insertable=false)
private String userId;
should do it
updatable=false, insertable=false might not be enough. Hibernate exposes additional requirement:
org.hibernate.DuplicateMappingException:
Table [passenger] contains physical column name [parent_id]
referred to by multiple logical column names: [parent_id], [parentId]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl$TableColumnNameBinding.bindPhysicalToLogical(InFlightMetadataCollectorImpl.java:1055)
The logical name parent_id comes from:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(insertable = false, updatable = false)
private Passenger parent;
while parentId comes from:
private Long parentId;
To harmonize logical names correct one (or set both) explicitly:
#Column(name = "parent_id")
private Long parentId;
Requirements for updatable=false, insertable=false comes from:
for ( Selectable columnOrFormula : value.getSelectables() ) {
if ( !columnOrFormula.isFormula() ) {
Column col = (Column) columnOrFormula;
if ( !distinctColumns.add( col.getName() ) ) {
throw new MappingException(
"Column '" + col.getName()
+ "' is duplicated in mapping for entity '" + getEntityName()
+ "' (use '#Column(insertable=false, updatable=false)' when mapping multiple properties to the same column)"
located:
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:1009)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:1027)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:1055)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:720)
at org.hibernate.mapping.RootClass.validate(RootClass.java:283)
at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:376)
I'm not expecting a concrete answer for this question because it's way too complex to explain in detail, just some guidance on where the problem could be.
Im summary, I have an entity Ship, with foreign keys to entities Origin, Destination and Country. I fetch a Ship from the database, then modify other field (date), and then query the database for related Origin, Destination and Country. When I query for Origin and Country, it goes as expected, but when I query for Destination, when executing query.getResultList() , and before the select a from DESTINATION, Hibernate automatically executes update SHIP set ... and it sets all the Ship fields except for IDN_DEST.
Any idea of what could this be happening?
My guess is that since the entity has been modified within the Session, Hibernates somehow things it needs to be updated, but that's all.
For what I've seen, there are no differences in the Ship.java:
// bi-directional many-to-one association
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "IDN_ORIGIN", insertable = false, updatable = false)
private Origin tOrigin;
#Column(name = "IDN_ORIGIN")
private Integer idnOrigin;
// bi-directional many-to-one association
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "IDN_DEST", insertable = false, updatable = false)
private Destination tDest;
#Column(name = "IDN_DEST", updatable = false)
private Integer idnDest;
// bi-directional many-to-one association
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "IDN_COUNTRY", insertable = false, updatable = false)
private Country tCountry;
#Column(name = "IDN_COUNTRY")
private Integer idnCountry;
About your question: Hibernate before a Query.list() perform a dirty check and automatically do a flush() to prevent inconsistent state.
To prevent that change Session.flushMode to COMMIT if you don't want automatic flush, but only at commit time.
About your example, two things:
Why you are not using a getter to get tOrigin, tCountry, tDest?
May you read tOrigin, tCountry, tDest before Ship update?
Tis is happening because when you select a Destination hibernate auto-flushes the session, so the state of the Ship is updated in the database. This means that when you modify other field (date) the ship object is attached to the session.
That should be not done only with Destination as tibtof told when you fetch data from database it creates persistent object and when you modify it hibernate will update it in database when it flushes connection, what you can try is after fetching Origin or Country flush session manually you should get updated data as what happened with 'Destination'.
When implementing composite primary keys in Hibernate or other ORMs there are up to three places where to put the insertable = false, updatable = false in composite primary key constellations that use identifying relationships (FKs that are part of the PK):
Into the composite PK class' #Column annotation (#Embeddable classes only) or
Into the entity class' association #JoinColumn/s annotation or
Into the entity class' redundant PK property's #Column annotation (#IdClass classes only)
The third is the only way to do with #IdClass and JPA 1.0 AFAIK. See http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Primary_Keys_through_OneToOne_Relationships. I will consider only cases 1. and 2.
Q:
Which way is the preferred place to put the "insertable = false, updatable = false" to generally?
I have experienced problems with Hibernate concerning this question. For example, Hibernate 3.5.x will complain about the Zips table
CREATE TABLE Zips
(
country_code CHAR(2),
code VARCHAR(10),
PRIMARY KEY (country_code, code),
FOREIGN KEY (country_code) REFERENCES Countries (iso_code)
)
with:
org.hibernate.MappingException: Repeated column in mapping for entity: com.kawoolutions.bbstats.model.Zip column: country_code (should be mapped with insert="false" update="false")
org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:676)
org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:698)
...
As you can see the country_code column is both PK and FK. Here are its classes:
Entity class:
#Entity
#Table(name = "Zips")
public class Zip implements Serializable
{
#EmbeddedId
private ZipId id;
#ManyToOne
#JoinColumn(name = "country_code", referencedColumnName = "iso_code")
private Country country = null;
...
}
Composite PK class:
#Embeddable
public class ZipId implements Serializable
{
#Column(name = "country_code", insertable = false, updatable = false)
private String countryCode;
#Column(name = "code")
private String code;
...
}
When putting the insertable = false, updatable = false into the entity class association's #JoinColumn all exceptions disappear and everything work fine. However, I don't see why the above code should not be working. It might be Hibernate having problems with this. Is the described a Hibernate bug, as it doesn't seem to evaluate #Column "insertable = false, updatable = false"?
In essence, what's the standard JPA way, the best practice, or preference where to put "insertable = false, updatable = false"?
Let me answer step by step.
1. When do you need ` insertable = false, updatable = false`?
Let's look at the below mapping,
public class Zip {
#ManyToOne
#JoinColumn(name = "country_code", referencedColumnName = "iso_code")
private Country country = null
#Column(name = "country_code")
private String countryCode;
}
Here we are referring to the same column in the table using two different properties. In the below code,
Zip z = new Zip();
z.setCountry(getCountry("US"));
z.setCountryCode("IN");
saveZip(z);
What will Hibernate do here??
To prevent these kind of inconsistency, Hibernate is asking you to specify the update point of relationships. Which means you can refer to the same column in the table n number of times but only one of them can be used to update and all others will be read only.
2. Why is Hibernate complaining about your mapping?
In your Zip class you are referring to the Embedded id class ZipId that again contains the country code. As in the above scenario now you have a possibility of updating the country_code column from two places. Hence the error given by Hibernate is proper.
3. How to fix it in your case?
No. Ideally you want your ZipId class to generate the id, so you should not add insertable = false, updatable = false to the countryCode inside the ZipId. So the fix is as below modify the country mapping in your Zip class as below,
#ManyToOne
#JoinColumn(name = "country_code", referencedColumnName = "iso_code",
insertable = false, updatable = false)
private Country country;
Hope this helps your understanding.
You can also solve this problem by using #PrimaryKeyJoinColumn annotation . The PrimaryKeyJoinColumn annotation specifies a primary key column that is used as a foreign key to join to another table.
The PrimaryKeyJoinColumn annotation is used to join the primary table of an entity subclass in the JOINED mapping strategy to the primary table of its superclass; it is used within a SecondaryTable annotation to join a secondary table to a primary table; and it may be used in a OneToOne mapping in which the primary key of the referencing entity is used as a foreign key to the referenced entity.
If no PrimaryKeyJoinColumn annotation is specified for a subclass in the JOINED mapping strategy, the foreign key columns are assumed to have the same names as the primary key columns of the primary table of the superclass.