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)
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
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).
I am using hibernate Reverse Engineering tool to generate pojo's from my database. Say I have two table's A and B in my database and another table ABMap which has two columns A_id and B_id that are foreign keys to table A and B respectively, and the primary key of ABMap is the composite key of A_id and B_id.
Now, when I build my project and generate the pojos, instead of ABMap being generated as a separate entity by hibernate, it is added into the entity A as a Set. Below is the snippet of code generated in entity A,
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = “ABMap”, schema= “myDB”, joinColumns = {
#JoinColumn(name = “A_id”, nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = “B_id”, nullable = false, updatable = false) })
public Set getBs() {
return this.bs;
}
public void setBs(Set bs) {
this.bs = bs;
}
Now the issue here is, using hibernate or Jpa I can do a insert into the ABMap table without actually having an entity of ABMap but I cannot update the same record since the updatable element in #JoinColumn is set to false by hibernate reverse engineering tool. Below is the sql error that occurs when an attempt is made to update the value of B_id.
2014-12-17 13:26:50,639 ERROR (qtp850520326-20) [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] - The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_A_B". The conflict occurred in database "myDB", table "B", column 'B_id'.
How can I set the updatable element in #JoinColumn to true?
I've got a table Category and a table TranslatableText. The category is like this
create table Category (
id int not null,
parent_id int default 0,
TranslatableDescriptionId int default 1,
primary key(id));
create table TranslatableText (
id int not null,
lang enum ('NO','EN','FR'),
text mediumtext,
primary key(id, lang));
In my Category entity I've defined a mapping:
#Fetch(FetchMode.SUBSELECT)
#Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
#OneToMany(fetch=FetchType.LAZY)
#JoinColumn(name="TranslatableDescriptionId")
#ForeignKey(name="FK_TranslatableTextId")
private Set<TranslatableText> translatableText;
But when it executes, it tries to access TranslatableDescriptionId, not id. Even if the TranslatableText entity has defined
#Id
#Column(name = "id", nullable = false)
private Integer id;
#Id
#Column(name = "lang", nullable = false)
#Enumerated(EnumType.STRING)
private String lang;
#Column(name = "text", length = 400, nullable = false)
private String text;
The query with the incorrect name selected:
select translatab0_.TranslatableDescriptionId as Translat4_13_1_, translatab0_.id as id1_, translatab0_.lang as Lang1_, translatab0_.id as id22_0_, translatab0_.lang as Lang22_0_, translatab0_.text as Text22_0_ from tblTranslateableText translatab0_ where translatab0_.TranslatableDescriptionId in ('126', '119', '103', '116', '121', '107', '113', '101', '109', '105', '123', '106', '125', '124', '114')
If I change the mappings #JoinColumn to read
#JoinColumn(name="TranslatableDescriptionId", referencedColumnName="id")
I get the following error when loading my app:
org.hibernate.MappingException: Unable to find column with logical name: id in org.hibernate.mapping.Table(Category) and its related supertables and secondary tables
For good measure I also tried:
#JoinColumn(name="id", referencedColumnName="TranslatableDescriptionId")
That gave me the error:
org.hibernate.MappingException: Unable to find column with logical name: TranslatableDescriptionId in org.hibernate.mapping.Table(Category) and its related supertables and secondary tables
Any suggestions to what I should do? I really want Category's translateableText to contain all the translations for its description, so I really want to join Category.TranslatableDescriptionId==TranslatableText.id
UPDATE1:
TranslatableText is used by many entities, so putting in a categoryId in it and reversing the relationship is not an option.
UPDATE2:
I was able to load it saying #JoinColumn(name="id"), but this led to a ClassCastException in Hibernate where it, instead of having an Integer as a key, has an Array containing a single Integer as a key. This fails to be made into a String and thus proper SQL. So it's probably still not the mapping I want
Cheers
Nik
This kind of mapping is possible, but not very convenient because you'll have to manage identity of TranslatableTexts manually (that's why Hibernate complains about non-mapped column TranslatableDescriptionId):
public class Category implements Serializable {
...
private Long translatableDescriptionId;
#OneToMany
#JoinColumn(name="id", referencedColumnName="TranslatableDescriptionId")
private Set<TranslatableText> translatableText;
...
}
So, you need to manually assign unique translatableDescriptionIds to all "targets" of TranslatableText (categories, items, folders as you say) and manually set this values as id of TranslatableText before persisting it (you can't just add TranslatableText into the Set).
--
However, the more convenient design is to introduce an intermediate entity to keep the identity of all transalations attatched to a specific target:
public class Category {
...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "targetId")
private TranslationTarget target;
}
public class TranslationTarget {
#Id #GeneratedValue
private Long id;
#OneToMany
#JoinColumn(name = "targetId")
private Set<TranslatableText> texts;
}
-
create table Category (
targetId int,
...);
create table TranslationTargets (
id int primary key
);
create table TranslatableText (
targetId int not null,
lang enum ('NO','EN','FR'),
text mediumtext,
primary key(targetId, lang));
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.