I'm attempting to create a base class for a set of entities to reduce coding effort and duplication. My thought is that the base class has the common meta-data fields, and the child classes deal with their unique attributes.
My base class:
#MappedSuperclass
public abstract class FinanceEntityBean {
protected Long id;
#Version
private long version;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
}
The first entity:
#Entity
#Table(name = "tag")
public class Tag extends FinanceEntityBean {
}
I've written tests using this code to do CRUD functions on the Tag entity, and they are all working fine.
My question is - why does Eclipse (Indigo) insist that Tag has an error:
The entity has no primary key attribute defined
I've changed that to a warning for now so my code will compile, but I'm curious why Eclipse isn't happy, and if I've misunderstood something.
Is this valid JPA 2.0 code? Hibernate 4.1.5 is my JPA provider.
When using mixed access you have to specify the access type. See Eclipse Dali bug 323527 for giving a better validation error when both fields and properties are annotated.
Option 1 : Annotate the getVersion() method instead, only properties are annotated.
Option 2 : Specify mixed access type as follows:
#MappedSuperclass
#Access(AccessType.PROPERTY)
public abstract class FinanceEntityBean {
protected Long id;
#Version
#Access(AccessType.FIELD)
private long version;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
}
If FinanceEntityBean is defined in a different Eclipse project from Tag, you may be suffering from the Dali bug "No primary key attribute in other plug-in project".
The workaround is to list FinanceEntityBean in the persistence.xml file associated with Tag.
I am fairly certain those are valid mappings.
The JPA 2.0 spec provides this example when talking about MappedSuperClasses (section 2.11.2):
#MappedSuperclass
public class Employee {
#Id protected Integer empId;
#Version protected Integer version;
#ManyToOne #JoinColumn(name="ADDR") protected Address address;
public Integer getEmpId() { ... }
public void setEmpId(Integer id) { ... }
public Address getAddress() { ... }
public void setAddress(Address addr) { ... }
}
// Default table is FTEMPLOYEE table
#Entity public class FTEmployee extends Employee {
// Inherited empId field mapped to FTEMPLOYEE.EMPID
// Inherited version field mapped to FTEMPLOYEE.VERSION
// Inherited address field mapped to FTEMPLOYEE.ADDR fk
// Defaults to FTEMPLOYEE.SALARY protected Integer salary;
public FTEmployee() {}
public Integer getSalary() { ... }
public void setSalary(Integer salary) { ... }
}
I had the same problem, but for a different reason, but I didn't realize it. In further research I found that in my case I had the MappedSuperclass in a different jar. According to User Gas https://stackoverflow.com/users/3701228/gas:
"According to JPA spec, if you have jpa classes in separate jar you should add it to the persistence.xml (I don't know, if Hibernate requires that, but you can give it a try). Try to add following entry to your persistence.xml entity.jar"
He references what is the right path to refer a jar file in jpa persistence.xml in a web app? as a description on how to do this.
I know this is late, but it is still an issue. Thank you #Kemoda stating in a comment to the question, this can be turned off in Eclipse preferences.
Easiest way to "correct" this is to go set preference as you see fit for your environment. I like "Warning" because in cases where the entity does not have a primary key is an error situation.
JPA
Errors/Warnings
Type
Entity has no primary key: [Error|Warning|Info|Ignore]
Related
I have the following setup with Spring Data JPA and Hibernate as the persistence provider. All of my entities inherit from a base class
#MappedSuperclass
public class BaseEntity {
#Id
private id;
#Version
private String version;
//more common fields
}
For example:
#Entity
public class Foo extends BaseEntity {
}
This leads to a primary key column with name "ID" to be generated on the "FOO" table. I would like to change the naming of the primary key column. It should reflect the name of class or table. So it should be "FOO_ID" instead of just "ID".
I know that I could do this statically by using #Column(name = "FOO_ID"). But that would mean I have to do this for every Entity. Is there a more dynamic way to achieve this?
I know this is an old question, but stumbled across this looking for an answer... Eventually found this solution elsewhere:
#Entity
#AttributeOverride(name="id", column=#Column(name="FOO_ID"))
public class Foo extends BaseEntity {
}
All your subClasses will have the same ID column name because of the inheritance, you can specify a common id colum name for all subClasses in the Base entity Class
Why use inheritance then? Just do it without inheritance.
You could use getters/setters to rename your fields
Ex:
class Foo {
private Long id;
public Long getFooId() {
return this.id;
}
public void setFooId(Long fooId) {
this.id = fooId;
}
}
My problems come from the fact that I am trying to reuse a mapped superclass that contains some basic fields such as a Long Id.
This mapped superclass looks like this:
#MappedSuperclass
public abstract class AbstractBaseEntity {
protected Integer id;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id", nullable = false, unique = true, columnDefinition = "int")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
It sits in a jar so that everyone can reuse it easily.
Apparently, all works perfectly except when entities that extend it actually have relationships between them and you try to get data using queries based on those relationships.
Example:
Say you have entity Organization that has one or more User (s):
#Entity
#Table(name = "Organizations")
public class Organization extends AbstractBaseEntity {
private Set<User> users;
#OneToMany(mappedBy = "organization", fetch = FetchType.LAZY)
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
#Entity
#Table(name = "Users")
public class User extends AbstractBaseEntity {
private Organization organization;
#ManyToOne
#JoinColumn(name = "Organization_ID", nullable = false)
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
}
Now here's the problem: If I use a DETACHED organization object as a parameter to a query like this:
SELECT u from User u where u.organization = ?1
then Hibernate throws this following exception:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.example.Organization
This doesn't make any sense to me, it shouldn't require an attached entity for this kind of query since all it needs is its id.
If, however, i take out AbstractBaseEntity from the jar and put it in the same project as Organization and User, it all works perfectly, with detached Organizations and all...
This looks very much like a bug to me. Any clues on how to work around it?
PS. I've tried explicitly specifying the AbstractBaseEntity as a class in persistence.xml as specified here - JPA #MappedSuperclass in separate JAR in Eclipse (weird but worth a try) ...doesn't work
Sorry to say, but I would assume you simply can not "pull" a MappedSuperclass from a different compilation unit. This is because the JPA provider maybe uses code instrumentation to access the entity fields.
Have you tried to create a clone class in your own workarea?
Regards from Germany,
Thomas
According to JPA spec, if you have jpa classes in separate jar you should add it to the persistence.xml (I don't know, if Hibernate requires that, but you can give it a try). Try to add following entry to your persistence.xml <jar-file>entity.jar</jar-file>
Check this post how to define path in the jar-file tag.
Just a guess, but entities need to implement Serializable. See if that fixes your issue.
I'm coming from a C# entity framework background and looking at JPA in a Java project so I'm hoping that what I'm facing is just a conceptual problem.
I've got a legacy database that I can't alter the schema of and I need to write a DAL.
I've generated (simplified for the example) the following entities...
#Entity
#Table(name = "crag", catalog = "rad_dbo")
public class CragEntity {
private int id;
#Column(name = "id")
#Id
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
private int fkSubRegionId;
#Column(name = "fk_subRegionId")
#Basic
public int getFkSubRegionId() {
return fkSubRegionId;
}
public void setFkSubRegionId(int fkSubRegionId) {
this.fkSubRegionId = fkSubRegionId;
}
}
and
#Table(name = "subRegion", catalog = "rad_dbo")
#Entity
public class SubRegionEntity {
private int id;
#Column(name = "id")
#Id
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
I've tried adding a relationship to CragEntity so that I can access its subRegion
#ManyToOne
#JoinColumn(name="fk_SubRegionId",nullable=false)
private SubRegionEntity subRegion;
but when I try to run
select c from CragEntity c where c.subRegion.region = :area
I get an exception
java.lang.RuntimeException: org.hibernate.QueryException: could
not resolve property: subRegion of: uk.co.bmc.rad.dal.CragEntity
Hopefully this is possible and I'm being slow...
Many thanks in advance for any help!
In your query you are searching for the property "subRegion" though in your entity definition you have the name "fkSubRegionId", so you must change the var name or the query. ;)
EDIT: Sorry i misreaded the relation.
Can you access the property (without making an HQL query) with the relationship inside the code?
Unless, you want to pick only certain fields in your query I would recommend a query like:
from CragEntity c where c.subRegion.region='theRegion'
It turns out there were several issues - one conceptual, one with how IntelliJ had generated a relationship I was copying and one between the chair and keyboard...
IntelliJ had picked the region to subregion relationship with the owner at the "wrong" end - probably a schema issue rather than IntelliJ's fault. Once I realised that and figured out the fix I could copy that to CragEntity and SubRegionEntity
In CragEntity I added:
private SubRegionEntity subRegion;
#ManyToOne
#JoinColumn(name="fk_SubRegionId",nullable=false)
public SubRegionEntity getSubRegion() {
return subRegion;
}
public void setSubRegion(SubRegionEntity subRegion) {
this.subRegion = subRegion;
}
and then in SubRegionEntity I added:
private List<CragEntity> crags;
#OneToMany(mappedBy = "subRegion")
List<CragEntity> getCrags() {
return crags;
}
public void setCrags(List<CragEntity> crags) {
this.crags = crags;
}
Also, it seem that any entity class that is going to be one end of a relationship has to implement serializable (I guess the entities get serialized into the owner. So that needed adding onto SubRegionEntity and RegionEntity
The silliness on my part was of course that the query should have been c.subRegion.region.name otherwise I was comparing an object of type RegionEntity with a string... doh - very stupid mistake on my part.
I'm new to TDD but as always as soon as I wrote tests for what I thought should be happening with the existing code I was walked through my errors (and given google keywords by the exceptions and errors :-))
I'm getting this Hibernate error:
org.hibernate.MappingException: Could not determine type for:
a.b.c.Results$BusinessDate, for columns: [org.hibernate.mapping.Column(businessDate)]
The class is below. Does anyone know why I'm getting this error??
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"businessDate"
})
#XmlRootElement(name = "Results")
#Entity(name = "Results")
#Table(name = "RESULT")
#Inheritance(strategy = InheritanceType.JOINED)
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Results implements Equals, HashCode
{
#XmlElement(name = "BusinessDate", required = true)
protected Results.BusinessDate businessDate;
public Results.BusinessDate getBusinessDate() {
return businessDate;
}
public void setBusinessDate(Results.BusinessDate value) {
this.businessDate = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"raw",
"display"
})
#Entity(name = "Results$BusinessDate")
#Table(name = "BUSINESSDATE")
#Inheritance(strategy = InheritanceType.JOINED)
public static class BusinessDate implements Equals, HashCode
{
....
Update: This code was generated by HyperJaxB. So I don't claim to understand it all, just trying to make some changes to it!
Update2: Here's the full (yah it's big) src file
Using a static nested class as a field type is fine and supported. But Hibernate won't know how to map such a complex type to a column type (which is what the error message says).
So you'll need either to create a user type to handle this or to annotate the Results.BusinessDate field with a #OneToOne annotation to persist it in another table (I would also remove the #Inheritance which is useless but this is not the problem here).
Update: Just to clarify, using a user type or mapping the complex type with #OneToOne does work. The following code works perfectly (tested):
#Entity
public class EntityWithStaticNestedClass implements Serializable {
#Id
#GeneratedValue
private Long id;
#OneToOne
private EntityWithStaticNestedClass.StaticNestedClass nested;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public EntityWithStaticNestedClass.StaticNestedClass getNested() {
return nested;
}
public void setNested(EntityWithStaticNestedClass.StaticNestedClass nested) {
this.nested = nested;
}
#Entity
public static class StaticNestedClass implements Serializable {
#Id
#GeneratedValue
private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
}
}
And both entities get well persisted in their respective tables. But you're not showing the entire code nor the exact error so I can't say why it didn't for you (maybe you're missing #Id etc).
That being said, if you don't want businessDate to be persisted at all, annotate it with #Transient (with JPA, fields are persistent by default):
Update: You can't mix field and property access. So you need to annotate getBusinessDate() with #Transienthere. Sorry, I couldn't guess that from the shown code and I thought it would be obvious.
Same comment as Kevin Crowell. You might also look at not using inner classes for entity types. I've actually never seen someone do that with Hibernate, so I'm not sure if it's even possible, or how you would map it.
The #Inheritance annotation on the BusinessDate inner class seems a little fishy too - the inner class is static, and does not inherit from another entity, unless Hibernate treats inner classes as "inherited."
Overall, not really sure what you're trying to accomplish, but you might be making your life harder than it should be. I would recommend not using inner classes, and just mapping all the entities in a more simple/straightforward fashion.
I'm considering using Annotations to define my Hibernate mappings but have run into a problem: I want to use a base entity class to define common fields (including the ID field) but I want different tables to have different ID generation strategies:
#MappedSuperclass
public abstract class Base implements Serializable {
#Id
#Column(name="ID", nullable = false)
private Integer id;
public Integer getId(){return id;}
public void setId(Integer id){this.id = id;}
...
}
#Entity
#Table(name="TABLE_A")
public class TableA extends Base {
// Table_A wants to set an application-defined value for ID
...
}
#Entity
#Table(name="TABLE_B")
public class TableB extends Base {
// How do I specify #GeneratedValue(strategy = AUTO) for ID here?
...
}
Is there some way to do this? I've tried including the following into TableB but hibernate objected to my having the same column twice and it seems wrong:
#Override // So that we can set Generated strategy
#Id
#GeneratedValue(strategy = AUTO)
public Integer getId() {
return super.getId();
}
In the code above, it looks like you're mixing annotations on fields (superclass) and methods (subclass). The Hibernate reference documentation recommends avoiding this, and I suspect it might be causing the problem. In my experience with Hibernate, it's safer and more flexible to annotate getter/setter methods instead of fields anyway, so I suggest sticking to that design if you can.
As a solution to your problem, I recommend removing the id field from your Base superclass altogether. Instead, move that field into the subclasses, and create abstract getId() and setId() methods in your Base class. Then override/implement the getId() and setId() methods in your subclasses and annotate the getters with the desired generation strategy.
Hope this helps.
On the method in the child dont add the second #Id tag.
#Override // So that we can set Generated strategy
#GeneratedValue(strategy = AUTO)
public Integer getId() {
return super.getId();
}
My resolution:
Refactor the Base class into:
#MappedSuperclass
abstract class SuperBase<K> {
public abstract K getId();
}
#MappedSuperclass
class Base<K> extends SuperBase<K> {
#Id #GeneratedValue(AUTO)
public K getId() { ... }
}
Then, you can extends from Base for most of your entity classes, and if one needs to override the #GeneratedValue, just extend from SuperBase and define it.
If you put your annotations on the getter rather than the field, when you override the method in the subclass, the annotations placed there will be used rather than the ones in the superclass.