I have a JPA entity with a property set as
#ManyToOne
#Column(name="LicenseeFK")
private Licensee licensee;
But when I deploy on JBoss 6 the application throws an error saying:
org.hibernate.AnnotationException: #Column(s) not allowed on a #ManyToOne property
I use Hibernate 3.5 as the JPA 2.0 implementation.
What should I use to reference the foreign key column?
Use #JoinColumn instead of #Column:
#ManyToOne
#JoinColumn(name="LicenseeFK")
private Licensee licensee;
#Column
The JPA #Column annotation is for basic entity attributes, like String, Integer, Date.
So, if the entity attribute name differs from the underlying column name, then you need to use the #Column annotation to specify the column name explicitly, like this:
#Column(name="created_on")
private LocalDate createdOn;
#JoinColumn
The #JoinColumn annotation is used to customize a Foreign Key column name, and it can only be used with an entity association.
So, in your case, because you are using a #ManyToOne association, you need to use #JoinColumn:
#ManyToOne(fetch=FetchTYpe.LAZY)
#JoinColumn(name="LicenseeFK")
private Licensee licensee;
Notice that we set the fetch attribute to FetchType.LAZY because, by default, FetchType.EAGER is used, and that's a terrible strategy.
Using #JoinColumn and #Column together will result in the same error.
Change it to only use: #JoinColumn to fix it.
In my case #VaishaliKulkarni's answer was helpful to identify the problem.
I missed to write field for #Column annotion and it effected on next field.
#Column(name = "account_id")
// I forgot to write field here
#ManyToOne
#JoinColumn(name = "customer_id")
private Customer customer;
So, I was getting exception at "customer" field.
Related
I have been going through some relational stuff in hibernate where I get this solution for relation between tables I tried this it works fine but when I remove #ForeignKey(name="FK_COUNTRY") nothing change then why are we using this annotation is that came under best practice?
#Entity
#Table(name = "state")
public class State {
#Id
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#ManyToOne
#ForeignKey(name="FK_COUNTRY")
private Country country;
}
Hibernate should reflect DB structure,
You should read about the importance of foreign key
Referential Integrity
Easier Detective Work
Better performance
If you read the javadoc of #ForeignKey, you'll find:
Used to specify the handling of foreign key constraints when schema generation is in effect. If this annotation is not specified, the persistence provider's default foreign key strategy will be used.
If you don't generate the database schema from the class definitions (e.g. CREATE TABLE SQL statement), then the annotation has no effect.
I'm using JPA2 with EclipseLink implementation
![Simple table structure][1]
Here are the two tables which I try to map and the JPA annotations.
public class Story implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
Integer id;
#Temporal(TemporalType.TIMESTAMP)
#Column (name="DATE_CREATED")
Date dateCreated;
String title;
String description;
#Column(name="AUTHOR_ID")
Integer authorId;
#Column(name="COUNTRY_ID")
Integer countryId;
private String reviews;
#OneToMany(mappedBy = "story", cascade=CascadeType.ALL)
private List<Tip> tipList;
}
public class Tip implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
private String description;
private Integer vote;
#ManyToOne (cascade=CascadeType.ALL)
#JoinColumn(name="STORY_ID", referencedColumnName="ID")
private Story story;
}
As a simple example I would like to persist a story and some story related tips in the same transaction.
Here is the section of code which does that:
Story newStory = new Story(title, body, ...);
EntityTransaction transaction = em.getTransaction().begin();
boolean completed = storyService.create(newStory);
//The tips are saved as a List<String>. This methods creates the needed List<Tip> from the Strings
List<Tip> tips = TipUtil.getTipList(tipList);
newStory.setTipList(tips)
transaction.commit();
I have no errors and all the entities are persisted in the database. The problem is that in the tip table the story_id field is always NULL. I can imagine that JPA is unable to get the new id from the story table. What's the correct approach here?
LE
In the current state of the code, the Tip entities are persisted but the country ID remains null.
With JPA, it is always recommended to update the relationship on both the sides in a bi-directional relationship. This is to ensure that the data is consistent in your application layer and nothing to do with the database.
However it is mandatory that you update the owning side of the relationship in a bidirectional relationship.
So, setting/not setting
story.setTipList(tips)
is up to you. But if you want the changes to reflect properly in DB then you mush call
tip.setStory(story)
as Tip is the owning side here, as per your code.
Also your code looks incomplete to me. Reasons is,
the entity returned by storyService.create(newStory) is managed but not the newStory. So just setting newStory.setTipList(tips) will not updated the db
Because you need to update the parent link story in each of your child.
The way its is done is to create a addTip(Tip tip) method in your Story class.
This method does :
tip.setStory(this);
tipList.add(tip);
If you don't need bedirectional approach, you can remove the story field in Tip and it will resolve your problem
Remove the
#Column(name = "STORY_ID")
private Integer storyId;
You are already declaring it in #JoinColumn(name="STORY_ID", referencedColumnName="ID")
That is why you are getting the error Multiple writable mappings exist for the field [tip.STORY_ID]
You should not be using PrimaryKeyJoinColumn, just JoinColumn, but having your complete class would help giving a certain answer.
PrimaryKeyJoinColumn would only be used if the story_id was also the id of the Tip (no id in Tip) and there was a duplicate basic mapping for it. It should rarely be used, and is not required in JPA 2.0 anymore as duplicate id mappings are no longer required.
I have seen a couple of examples on the Internet about using JoinColumn like the way in the example below.
Actually, there are two questions I want to ask you about this partcular example. Can't we just get rid of the "optional" parameter by adding a "nullable=false" parameter to the JoinColumn. Are there any differences between optional and nullable entity relationship-wise?. What are the advantages of setting insertable and updatable to false in the joincolumn? Is this done to ensure that Employee entity cannot update the Department entity?
#Entity
public class Employee {
// ...
#ManyToOne(optional=false)
#JoinColumn(name="DEPT_ID", insertable=false, updatable=false)
private Department department;
// ...
}
optional = false on the ManyToOne is a runtime instruction for the persistence framework to interpret when building java objects. nullable = true on the JoinColumn is for setting up the database schema and may or may not be interpreted at runtime depending on your persistence provider.
Normally that JoinColumn mapping is done that way to indicate that the relationship is managed from the other side, by the Department.
You would likely have:
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="DEPT_ID")
private Set<Employee> employees;
Over on your department, and you'd create new employee-department relationships by adding them to the collection there, rather than setting a Department on the Employee. That's why you mark the Employee's department as non-updatable, because the Department "owns" the ability to modify the relationship.
I have two tables: t_promo_program and t_promo_program_param.
They are represented by the following JPA entities:
#Entity
#Table(name = "t_promo_program")
public class PromoProgram {
#Id
#Column(name = "promo_program_id")
private Long id;
#OneToMany(cascade = {CascadeType.REMOVE})
#JoinColumn(name = "promo_program_id")
private List<PromoProgramParam> params;
}
#Entity
#Table(name = "t_promo_program_param")
public class PromoProgramParam {
#Id
#Column(name = "promo_program_param_id")
private Long id;
//#NotNull // This is a Hibernate annotation so that my test db gets created with the NOT NULL attribute, I'm not married to this annotation.
#ManyToOne
#JoinColumn(name = "PROMO_PROGRAM_ID", referencedColumnName = "promo_program_id")
private PromoProgram promoProgram;
}
When I delete a PromoProgram, Hibernate hits my database with:
update
T_PROMO_PROGRAM_PARAM
set
promo_program_id=null
where
promo_program_id=?
delete
from
t_promo_program
where
promo_program_id=?
and last_change=?
I'm at a loss for where to start looking for the source of the problem.
Oh crud, it was a missing "mappedBy" field in PromoProgram.
Double-check whether you're maintaining bidirectional association consistency. That is; make sure that all PromoProgramParam entities that link to a PromoProgram as its parent are also contained in said parent's params list. It's a good idea to make sure this happens regardless of which side "initiates" the association if you will; if setPromoProgram is called on a PromoProgramParam, have the setter automatically add itself to the PromoProgram's params list. Vice versa, when calling addPromoProgramParam on a PromoProgram, have it set itself as the param's parent.
I've encountered this problem before as well, and it was due to not maintaining bidirectional consistency. I debugged around into Hibernate and found that it was unable to cascade the delete operation to the children because they weren't in the list. However, they most certainly were present in the database, and caused FK exceptions as Hibernate tried to delete only the parent without first deleting its children (which you've likely also encountered with the #NonNull in place).
FYI, I believe the proper "EJB 3.0"-way of making the PromoProgramParam.promoProgram field (say that a 100 times) non-nullable is to set the optional=false attribute on the #ManyToOne annotation.
When trying to save an ID from my parent class into a child class, I keep getting the error
"ERROR - Field 'parent_id' doesn't have a default value"
I have tried all types of mappings. I am using annotations.
Any help on this would be appreciated
Parent:
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="description")
private String description;
#OneToMany
#Cascade(value= {org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE})
#JoinColumn(name="parent_id")
private List<Child> children;
Child:
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="description")
private String description;
Thanks.
A late addition in case anyone ever runs into the same issue.
This entity here, when persisted using Hibernate 4.1.8, will cascade the FieldChangeentities, but will not fill the join column:
#Entity
public class Event {
//ID and other fields here
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "event_id")
private List<FieldChange<?>> fields = new ArrayList<FieldChange<?>>();
}
Neither does the insert statement set the event_id column, nor does it update the inserted entity after the fact - the event_id remains null and the relation is lost.
If, however, the #JoinColumn definition is changed like this:
#JoinColumn(name = "event_id", nullable = false)
, then the insert statement includes the event_id column like it should, and all is well.
This may only be a regression in this particular version of Hibernate, but maybe it helps someone.
In your case JPA provider to persist child object with its parent perform at least three queries on db. First two persist the objects by its own. The last one
update child object with the foreign key referencing parent. The second query fail because you have a NOT NULL constraint on the foreign key column. You have three options:
Remove NOT NULL constraint on foreign key in the child entity
Use bidirectional relationship
Change JPA provider to one which supports such cases.
You must have something wrong somewhere else because those mappings will work the way they are. They could be better, but they'll work. Specifically, all the #Column annotations are redundant and unnecessary, and as non sequitor noted, you should use the cascade property of JPA's #OneToMany instead of Hibernate's #Cascade. I've created a runnable example with the cleaned-up version of what you posted. If you have git and maven, you can run it with:
git clone git://github.com/zzantozz/testbed tmp
cd tmp
mvn -q compile exec:java \
-Dexec.mainClass=rds.hibernate.UnidirectionalManyToOneJoinColumn \
-pl hibernate-unidirectional-one-to-many-with-join-column
It creates a parent with two children, saves them, and then loads them and prints out the graph. The output is:
Creating parent with two children
Loading saved parent
Parent{description='parent', children=[Child{description='child 2'}, Child{description='child 1'}]}
Change your #OneToMany to #OneToMany(cascade=CascadeType.ALL) use JPA rather than the Hibernate extensions
My guess is that the #JoinColumn annotation needs a referencedColumnName assigned.
#JoinColumn(name = "parent_id", referencedColumnName = "id")