I am getting this error when I will persist() my entity. I think that the cause of the error is the relation, my idea is that FolderEntity (represents a virtual folder) can be stay inside another (only one) Then I created the reference to self (In the extended class, because all resources can be inside a folder, and folder is an resource)
org.hibernate.AnnotationException: Referenced property not a (One|Many)ToOne: com.editor.entity.FolderEntity.id in mappedBy of com.editor.entity.FolderEntity.folderId
This my main Entity:
#MappedSuperclass
public abstract class Entity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", nullable = false)
private Integer id;
/** getter/setter **/
}
Then I extends it in my ResourceEntity Entity:
#MappedSuperclass
public class ResourceEntity extends Entity {
#Column(name = "NAME", length = Lengths.NAME40, unique = true, nullable = false)
private String name;
#Column(name = "DESCRIPTION", length = Lengths.DESCRIPTION1000, unique = false, nullable = true)
private String description;
#JoinColumn(name = "FOLDER_ID", updatable = true, nullable = false)
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "id")
private FolderEntity folderId;
/** getter/setter **/
}
Finally, I am working with this entity:
#javax.persistence.Entity
#Table(name = "EDITOR_FOLDERS")
#NamedQueries({
#NamedQuery(name = FolderEntity.ALL_FOLDERS, query = "select f from FolderEntity f"),
#NamedQuery(name = FolderEntity.FOLDER_BY_NAME, query = "select f from FolderEntity f where name = :name and resourceType = :resourceType") })
public class FolderEntity extends ResourceEntity {
public static final String ALL_FOLDERS = "findAllFolders";
public static final String FOLDER_BY_NAME = "findAllFoldersByName";
#Column(name = "RESOURCE_TYPE", length = Lengths.CODE, unique = false, nullable = false)
private Integer resourceType;
/** getter/setter **/
}
Anybodys help me to solve this? Thanks!
You should check the meaning of mappedBy: It does not reference the field that contains the ID (JPA is clever enough to find that one by itself), but it references another XToOne field that "owns" the mapping
public abstract String mappedBy
(Optional) The field that owns the relationship. This element is only specified on the inverse (non-owning) side of the association.
(from javadoc of OneToOne)
In your case you don't need the mappedBy as you are on the owning side. And you should name the attribute folder as you are referencing no ID but an entity.
Another remark: Use an enum for resourceType if you intend to define the possible values in your application as constants.
Related
Given A class
#Entity
#Table(name = "ATABLE")
public class A implements Serializable {
public static final String DB_ID = "AID";
public static final String DB_MARKET = "AMARKET";
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "id", column = #Column(name = DB_ID)),
#AttributeOverride(name = "market", column = #Column(name = DB_MARKET))
})
public AIdClass id;
#OneToMany
#JoinColumn(name = B.DB_MARKET, referencedColumnName = DB_MARKET, insertable = false, updatable = false)
public List<B> bs;
}
and B class
#Entity
#Table(name = "BTABLE")
public class B implements Serializable {
public static final String DB_ID = "BID";
public static final String DB_MARKET = "BMARKET";
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "id", column = #Column(name = DB_ID)),
#AttributeOverride(name = "market", column = #Column(name = DB_MARKET))
})
public BIdClass id;
}
Each entities might be listed, but using that #OneToMany relation does throw the following error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory'
Caused by: org.hibernate.AnnotationException: Unable to map collection fr.zzz.domain.A.bs
Caused by: org.hibernate.AnnotationException: referencedColumnNames(AMARKET) of fr.zzz.domain.A.bs referencing fr.zzz.domain.B not mapped to a single property
An A entity relates to multiple B on A.AMARKET = B.BMARKET
You are having this issue because there is be a possibility that composite keys (AID,AMARKET) and (BID,BMARKET) will not be unique when doing a join on keys AMARKET = BMARKET. Therefore you are getting the error not mapped to a single property. Please bear with me, use the following sample data to analyze the issue;
For table A
AID AMARKET
1 1
2 1
3 2
For table B
BID BMARKET
1 1
2 2
3 2
The above scenario is absolutely possible (at least on a database level) and just using AMARKET and BMARKET to make the join #OneToMany is not possible. What is possible though is to use #ManyToMany, this will immediately solve the issue if the table structures are correct.
But what if it is required to use #OneToMany due to some business constraint. Then you must update the table B to include A.AID and add a foreign key constraint to ensure data integrity. Then only the result set will be valid for the relationship #OneToMany. And the join will be as follows;
#OneToMany
#JoinColumn(name = B.DB_AID, referencedColumnName = DB_ID)
#JoinColumn(name = B.DB_MARKET, referencedColumnName = DB_MARKET)
public List<B> bs;
In B:
#Entity
#Table(name = "BTABLE")
public class B implements Serializable {
public static final String DB_ID = "BID";
public static final String DB_MARKET = "BMARKET";
public static final String DB_AID = "AID";
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "id", column = #Column(name = DB_ID)),
#AttributeOverride(name = "market", column = #Column(name = DB_MARKET))
})
public BIdClass id;
#Column(name = DB_AID)
private Long aid; // assuming aid is a Long
}
Now the join is being done on the composite primary key of A.
I have an entity BlocRecord having a composite code BlocRecordId, and in its composite code there is an #Embedded (relation code ManyToOne) pointing to another entiy Record and want to Audit the entity BlocRecord.
The entity BlocRecord
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "blocRecord")
#Access(value = AccessType.FIELD)
#Audited
public class BlocRecord {
#EmbeddedId
private BlocRecordId blocRecordId = new BlocRecordId();
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumns({
#JoinColumn(name = "record_identifier_", referencedColumnName = "identifier_", unique = false, nullable = false),
#JoinColumn(name = "record_recordType_", referencedColumnName = "recordType_", unique = false, nullable = false)})
#MapsId("record")
private Record record;
...
}
The id class BlocRecordID
#Embeddable
public class BlocRecordId implements Serializable {
#Embedded
private RecordId record;
#Column(name = "source_")
String source ;
#Column(name = "messageType_")
String messageType ;
The entity Record
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "records")
#Access(value = AccessType.FIELD)
#Audited
public class Record {
#EmbeddedId
private RecordId recordId = new RecordId();
#OneToMany(targetEntity = BlocRecord.class, fetch = FetchType.LAZY, mappedBy = "record")
private Set<BlocRecord> blocRecord = new java.util.HashSet<>();
...
}
The idClass of the entity Record
#Embeddable
public class RecordId implements Serializable{
#Column(name = "identifier_")
String identifier ;
#Column(name = "recordType_")
String recordType ;
}
Hibernate-envers fails when trying to generate the metadata of the field record in the embeddable BlocRecordId, the The flowing exception is thrown
org.hibernate.MappingException: Type not supported: org.hibernate.type.ComponentType
at org.hibernate.envers.configuration.internal.metadata.IdMetadataGenerator.addIdProperties(IdMetadataGenerator.java:121)
at org.hibernate.envers.configuration.internal.metadata.IdMetadataGenerator.addId(IdMetadataGenerator.java:230)
at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.generateFirstPass(AuditMetadataGenerator.java:642)
at org.hibernate.envers.configuration.internal.EntitiesConfigurator.configure(EntitiesConfigurator.java:95)
at org.hibernate.envers.boot.internal.EnversServiceImpl.doInitialize(EnversServiceImpl.java:154)
at org.hibernate.envers.boot.internal.EnversServiceImpl.initialize(EnversServiceImpl.java:118)
at org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl.produceAdditionalMappings(AdditionalJaxbMappingProducerImpl.java:99)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:288)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:83)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:417)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:86)
at org.hibernate.boot.MetadataSources.buildMetadata(MetadataSources.java:179)
Do you have any idea how to resolve the issue ?
Thanks
At the moment, Envers does not support the idea of nesting an embeddable inside an embeddable when we map the identifier columns like your example illustrates. The only valid mappings that Envers presently does support is if the attribute in the embeddable is a #ManyToOne or a #Basic type.
You can work around this problem but it involves being a bit more explicit and not using RecordId. What I mean is rewrite BlocRecordId to be the following:
#Embeddable
public class BlocRecordId implements Serializable {
#Column(name = "identifier_")
String identifier;
#Column(name = "recordType_")
String recordType;
#Column(name = "source_")
String source;
#Column(name = "messageType_")
String messageType;
#Transient
private RecordId recordId;
/** Helper method to assign the values from an existing RecordId */
public void setRecordId(RecordId recordId) {
this.identifier = recordId.getIdentifier();
this.recordType = recordId.getRecordType();
}
/** Helper method to get the RecordId, caching it to avoid multiple allocations */
public RecordId getRecordId() {
if ( recordId == null ) {
this.recordId = new RecordId( identifier, recordType );
}
return this.recordId;
}
}
I agree this is less than ideal but it does at least work around the current limitation of the code. I have gone added and added HHH-13361 as an open issue to support this. You're welcomed to contribute if you wish or I'll work in getting this supported for Envers 6.0.
I am experiencing a problem with hibernate and lazy loading of objects.
basically I want to load an class which has an eagerly loaded field and not load the lazy fields of child classes
Take the following QuestionVO class
#Entity
#Table(name = "question")
public class QuestionVO extends BaseDAOVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = -5867047752936216092L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column(name = "questionText", unique = false, nullable = false, length = 4000)
#Size(min = 3, max = 4000)
#Pattern(regexp = MobileAppsRegexConstants.GENERAL_ALLOWED_CHARCHTERS, message = "Question Text Not valid.")
private String questionText;
#ManyToOne(fetch = FetchType.EAGER)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name = "MENU_STYLE_ID", nullable = true)
private MenuStyleVO menuStyle;
}
Take the following MenuStyleVO class
#Entity
#Table(name = "menu_style")
public class MenuStyleVO extends BaseDAOVO implements Serializable{
/**
*
*/
private static final long serialVersionUID = 3697798179195096156L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column(name = "menuStyleName", unique = false, nullable = false, length = 200)
private String menuStyleName;
#Column(name = "menuTemplate", unique = false, nullable = false, length = 200)
private String menuTemplate;
#OneToOne(fetch = FetchType.LAZY, optional=false)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name="logo_id")
#JsonProperty("logo")
private ApplicationImageVO logo;
}
And this ApplicationImageVO class
#Entity
#Table(name = "application_image")
public class ApplicationImageVO extends BaseDAOVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = -9158898930601867545L;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "image1242x2208")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private SubmissionLauncherImagesVO launcherImage1242x2208;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "image1536x2048")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private SubmissionLauncherImagesVO launcherImage1536x2048;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "image2048x1536")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private SubmissionLauncherImagesVO launcherImage2048x1536;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "logo")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private MenuStyleVO menuStyleLogo;
}
If L load the QuestionVO class from the database using the following hibernate criteria code - all the lazy fields of MenuStyleVO and ApplicationImageVO are also loaded.
On complicated use cases, this results in this query getting very slow
public QuestionVO findMasterAppQuestionById(int id) {
Criteria criteria = currentSession().createCriteria(QuestionVO.class);
criteria.add(Restrictions.eq("id", id));
QuestionVO questionVO = (QuestionVO) criteria.uniqueResult();
return questionVO;
}
What I am wondering is - would it be possible to load the QuestionVO class and its eager fields and tell hibernate to ignore lazy fields from the other classes bar those that are needed?
Cheers
Damien
Last time we faced an issue like this we used a constructor on parent class, which use only the desired fields of determined query.
I can't remember in fully how constructor inside a jpql query works, but it must be something like this:
select new com.package.class(c.field1, c.field2) from com.package.class c
Remember, a constructor with same arguments must be present on the desired entity.
Pros:
- Better query perfomance;
- Can be replicated with other arguments;
Cons:
- Pretty limited, you can only use this hack on the main entity you are querying;
- Includes a constructor only for determined query, poor design;
Also, you should take a look on EnttyGraphs of JPA. Seems quite promising, but didn't work as desired in our project.
Btw, Hibernate has put us many times on performance issues, hope this hack help you, good luck!
Edit:
Why this pattern would help in performance issues?
Basically, with the example i've showed before, you are not loading everything via Hibernate, only the two fields (field1 and field2) of the main entity. Without using a constructor you shoudn't be able to do that, because your query would not result in a collection of the desired entity, but in a collection of two objects each iteration (Object[]). Using the constructor pattern you are creating instances of the desired entity, but only selecting a few fields from database, and that's why this pattern can help you, you are returning a collection of the desired entity with only a few fields.
I just refactor a Project to use Hibernate (4.2.4.Final) with Inheritance. But I got trouble with ManyToMany annotation.
I have a base File Class like this:
#Entity
#Table(name = "file")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "descriminator", length = 25)
public abstract class File {
#Id
#Column(name = "id", unique = true, nullable = false, length = 256)
private String id;
}
and a special Inheritance class like this:
#Entity
#DiscriminatorValue("ISSUE_HISTORY_ATTACHMENT")
#Data
public class IssueHistoryAttachment extends File {
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "issue_history_attachment", joinColumns = {
#JoinColumn(name = "attachment_id", nullable = false, unique = true) }, inverseJoinColumns = {
#JoinColumn(name = "issue_history_id", nullable = false)})
private IssueHistory history;
}
This IssueHistoryAttachment Class is also referenced in my IssueHistory Class.
#Entity
#Table(name = "issue_history")
#TableGenerator(name="tg", table="hibernate_sequences",pkColumnName="sequence_name", valueColumnName="sequence_next_hi_value", allocationSize=1)
public class IssueHistory implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "tg")
#Column(name = "id", unique = true, nullable = false)
private int id;
// some other fields
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "issue_history_attachment", joinColumns = {
#JoinColumn(name = "issue_history_id", nullable = false)
}, inverseJoinColumns = {
#JoinColumn(name = "attachment_id", nullable = false, unique = true)
})
private Set<IssueHistoryAttachment> attachments = new HashSet<IssueHistoryAttachment>();
}
When i now store a IssueHistory Instance with two Attachments, all this fields are correctly saved in my database.
I got 2 new entries in the file table, one new entry in the *issue_history* table and two correct entries in the relation table *issue_history_attachment*.
So at this points all thinks are looking fine. But when I try to read the Values Attachment Set in the IssueHistory Instance only contains one element instead of two like stored in the database.
Any suggestions how to solve this?
I just found the source of the Problem.
It was a missing/wrong equals method. :-)
I can't comment yes so I have to make an answer.
I see one problem in your code (or maybe I don't understand it):
In IssueHistory you are using #ManyToMany to IssueHistoryAttachment but in IssueHistoryAttachment you are using #ManyToOne.
In my opinion it is the reason of your problem.
I am using:
Spring 3.2
Hibernate 4.1.9
I need to map, with JPA, three classes. Class A has a ManyToMany relationship with Class B. A unique combination of Class A and Class B need to own a collection of Class C.
Table A
foo
id | name
Table B
bar
id | name
Table C
data
id | xrefId
Join Table -- Unique Key on (fooId,barId)
xref
id | fooId | barId
Altering the existing data structure is not an option.
Edit 1:
Goal: Load a Foo, get its collection of Bars. From each Bar, get its (their!) collection of Data.
Class A
#Entity
public class Foo {
#Id
private UUID id;
#ManyToMany(optional = false)
#JoinTable(name = "xref",
joinColumns = { #JoinColumn(name = "fooId") },
inverseJoinColumns = { #JoinColumn(name = "barId") })
private List<Bar> lstBar = new ArrayList<Bar>();
}
Class B
public class Bar {
#Id
private UUID id;
#ManyToMany(mappedBy = "lstBar")
private List<Foo> lstFoo = new ArrayList<Foo>();
}
Class C
public class Data {
#Id
private UUID id;
}
Just KISS. Make another class Xref, which contains id, foo, bar and Set<Data> fields. Make a DAO method to find an Xref using two parameters foo and bar (implement it with a simple HQL). The unique requirement could be achieved by an unique constraint in the database.
It doesn't look good trying to express it just by the class hierarchy, better to use DAOs.
Your join table, xref, has an extra id field, in order to be able to create such a table with JPA you need an extra entity class XRef and then you have to map the relation between A and XRef and betweem B and XRef (both are one-to-many). Then, you can create the entity class C and map the relation between C and XRef. Do you need more help? I don't have time right now to provide the code, but if you need ask and I will try to add it as soon as possible.
Look at this example (used Integer instead of UUID for simplicity, the rest should be OK).
Bar class:
public class Bar {
#Id
private Integer id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "barId")
private Collection<Xref> xrefCollection;
}
Foo class:
public class Foo {
#Id
private Integer id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "fooId")
private Collection<Xref> xrefCollection;
}
Xref class:
public class Xref {
#Id
private Integer id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "xrefId")
private Collection<Data> dataCollection;
#JoinColumn(name = "bar_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Bar barId;
#JoinColumn(name = "foo_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Foo fooId;
}
Data Class:
public class Data {
#Id
private Integer id;
#JoinColumn(name = "xref_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Xref xrefId;
}
This code has been automatically generated by NetBeans, provided that all tables and indexes are correctly defined in the DB