how to get OneToMany relationship revision in Hibernate envers - hibernate-envers

we have two entities like Resource and NonFTECost. And we have relationship between these is OneToMany bi-directional. below is the entities
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table
public class Resource {
#NotNull
#Column(name = "NAME", nullable = false, length = 255)
#Audited(withModifiedFlag = true)
private String name;
#OneToMany(mappedBy = "resource", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#Audited
private List<NonFTECost> costings = new ArrayList<>();
//other fields
}
#Getter
#Setter
#Table
#Entity
public class NonFTECost {
#NotNull
#Audited(withModifiedFlag = true, modifiedColumnName = "PAYMENT_MOD")
private Payment payment;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "RESOURCE_CODE", nullable = false, foreignKey = #ForeignKey(name = FK_COST_DETAILS_RESOURCE_CODE))
#Audited(withModifiedFlag = true, modifiedColumnName = "RESOURCE_CODE_MOD", targetAuditMode = RelationTargetAuditMode.AUDITED)
private Resource resource;
//other fields
}
now i create the one resource wiht costings, then it will create new revision for each audit table. And then i changed only payment field of NonFTECost entity, it will create new revision in NonFTECost_Aud table(it is also the part of resource update).
Question:- While getting the revision of resource, i want to get revisions of NonFTECost for that perticular resource entity. because of i want to show to user like fieldName oldvalue newvalue
Please help me to sort out the issue.

You should be able to fetch the associated NonFTECost entities for a given revision of Resource by iterating that collection on the specific revision instance you queried.
For example, lets say I was interested in revision 5 of Resource
final Number revision = 5;
final AuditReader auditReader = auditReaderFactory.get( session );
final Resource resource = auditReader.find( Resource.class, resourceId, revision );
for ( Cost cost : resource.getCosts() ) {
// do whatever with cost
}
Now what you need is how to take that Cost instance and find out what changed. Since you use the feature withModifiedFlags=true, this allows us to make special use of forRevisionsOfEntityWithChanges.
The one thing I want to point out is that it is possible in your mapping scenario that a NonFTECost entity could have a higher revision than that of your Resource if you happen to modify the cost entity in a transaction where no modification happens specifically to Resource .
With that in mind, you'll need to account for that in the for-loop logic. So inside that loop, we'll need to execute a query based on the Cost instance and fetch its revision history separately.
// This list is an object array that contains the following
// Index 0 - The `Cost` entity again at the revision
// Index 1 - The revision entity (contains revision number/timestamp)
// Index 2 - The RevisionType: ADD, MOD, or DEL
// Index 3 - Set<String> of property names that changed at this revision
List results = auditReader.createQuery()
.forRevisionsOfEntityWithChanges( Cost.class, false )
.add( AuditEntity.id().eq( cost.getId() ) )
.addOrder( AuditEntity.revisionNumber().desc() )
.setMaxResult( 2 )
.getResultList();
If the results list only contains 1 row, then you know a couple of things (assuming no data pruning)
index 2 should be RevisionType.ADD
Index 3 contains Set<String> of fields set during the original persist.
There are no fields that have an old value, just whatever their insert persist value was.
If the results list contains 2 rows, then this is where you'll need to handle old and new value logic. You should be able to do something like this:
if ( results.size() == 2 ) {
Object[] newArray = (Object[]) results.get( 0 );
Object[] oldArray = (Object[]) results.get( 1 );
Set<String> propertiesChanged = (Set<String>) newArray[3];
for ( String propertyName : propertiesChanged ) {
if ( "value".equals( propertyName ) ) {
Double newValue = ( (NonFTECost) newArray[0] ).getValue();
Double oldValue = ( (NonFTECost) oldArray[1] ).getValue();
}
}
}
else if ( results.size() == 1 ) {
Object[] array = (Object[]) results.get( 0 );
Set<String> propertiesChangedOnInsert = (Set<String>) array[3];
// do whatever here
}
This isn't the most elegant of solutions, but it works.

Related

Single jpa #Query to return true/false when count in one table is equal to column value in other table

I have 2 entities: Leaflet and Page with One to Many relation (many Pages per Leaflet)
#Entity
Leaflet {
#Id
#GeneratedValue
private UUID leafletId;
private Integer noPages;
#OneToMany(mappedBy = "leaflet", cascade = CascadeType.ALL, orphanRemoval = true)
Set<Page> pages = new HashSet<>();
}
#Entity
Page {
#Id
#GeneratedValue
private UUID pageId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "leaflet_id")
private Leaflet leaflet;
#Enumerated
private PageStatus status = PageStatus.CREATED;
}
and status enum
public enum PageStatus {
CREATED,
FRAMED
}
I would like to write single query to return whether all Pages for given Leaflet are already FRAMED. So I wrote this
#Repository
public interface PageRepository extends JpaRepository<Page, UUID> {
#Query("SELECT case when (COUNT(p) = l.noPages) then true else false end from Page p inner join Leaflet l on p.leaflet.leafletId = l.leafletId where p.status = 1 and l.leafletId = ?1")
boolean allPagesFramed(UUID leafletId);
}
but error comes which means I cannot use l.noPages directly
ERROR: column "leaflet1_.no_pages" must appear in the GROUP BY clause or be used in an aggregate function
org.hibernate.exception.SQLGrammarException: could not extract ResultSet
Is there a way to make it 1 query ?
Of course, I can first select l.noPages with first hit to DB, then inject this value to above query (instead of join) which I'm doing right now as workaround.
You can do this based on page table. With nativeQuery = true
#Query(value = "select case when ( count(*) > 0 ) then false else true end " +
"from page p " +
"where p.leaflet_id = ?1 and p.status <> 1 ", nativeQuery = true)
boolean allPagesFramed(UUID leafletId);
If a page has at least one status different from 1 (FRAMED), then the query return false, not all the pages are FRAMED.

Cascade delete problem on **custom** unidirectional #OneToMany relationship (JPA eclipselink)

I'm having problem deleting an entity having an Unidirectional #OneToMany custom relationship.
Here the relationship on the "base" entity (relevant columns only):
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "Id", updatable = false)
protected Integer id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "ObjectId", referencedColumnName = "Id")
protected Collection<Attachment> attachmentsCollection;
Here the relevant columns on the "child" entity:
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "Id", updatable = false)
protected Integer id;
#NotNull
#Basic(optional = false)
#Size(min = 1, max = 64)
#Column(name = "ObjectTable", updatable = false)
protected String objectTable;
#Basic(optional = false)
#Column(name = "ObjectId", updatable = false)
protected Integer objectId;
#NotNull
#Basic(optional = false)
#Lob
#Column(name = "Data")
protected byte[] data;
#NotNull
#Basic(optional = false)
#Column(name = "SizeInBytes")
protected Long sizeInBytes;
#NotNull
#Basic(optional = false)
#Size(min = 1, max = 128)
#Column(name = "Name")
protected String name;
Here explanation on why this is a custom relationship:
The base object is the super class of all entities. It has, additional to its own id, the opportunity to associate any number of attachments via the attachments collection. Since Ids are not unique between tables (entities), it's needed to add an additional column in the child table (the attachment table). This additional column (ObjectTable) identifies the entity-kind owning the attachment. Adding this column to the entity'id (ObjectId) column, the relation is complete:
Suppose record 99 of entity Invoice has 2 attachments (attachment 'Z' and attachment 'Y'):
Table Invoice
-------------
Id ColumnA ColumnB ColumnC...
99 'xyz' '2343' 'zyx'
.
.
Table Attachment
----------------
Id ObjectTable ObjectId Data SizeInBytes Name
43542 'Invoice' 99 11100110 437834 'Z.pdf'
43543 'Invoice' 99 101110 867454 'Y.pdf'
I managed to load the relation with a mapping customizer:
public static final String TABLENAME = "TECAttachment";
public static final String OBJECTIDFIELDNAME = TABLENAME + ".ObjectId";
public static final String OBJECTTABLEFIELDNAME = TABLENAME + ".ObjectTable";
// Customize how records are selecting inside entity's attachments collection: include the entities table name
#Override
public void customize(ClassDescriptor descriptor) {
OneToManyMapping mapping = (OneToManyMapping)descriptor.getMappingForAttributeName(AttachmentEntitySessionCustomizer.ATTACHMENTSCOLLECTIONNAME);
ExpressionBuilder eb = new ExpressionBuilder();
Expression eObjectIdNotNull = eb.getField(AttachmentEntitySessionCustomizer.OBJECTIDFIELDNAME).notNull();
Expression eObjectId = eb.getField(AttachmentEntitySessionCustomizer.OBJECTIDFIELDNAME).equal(eb.getParameter(descriptor.getPrimaryKeyFields().get(0)));
Expression eObjectTable = eb.getField(AttachmentEntitySessionCustomizer.OBJECTTABLEFIELDNAME).equalsIgnoreCase(descriptor.getTableName());
mapping.setSelectionCriteria(eObjectIdNotNull.and(eObjectId.and(eObjectTable)));
}
... but I'm having problems during the delete operation of any entity. For a reason that I still don't understand, JPA is executing an update statement over the Attachment table without taking in consideration the ObjectTable column. Here is what's going on when I delete a record from the table PRHTABidParticipationItem:
Finest: Execute query DeleteObjectQuery(com.tec.uportal.prhta.model.bid.participation.PRHTABidParticipationItem[ id=24 ])
Finest: Execute query DataModifyQuery()
Fine: UPDATE TECAttachment SET ObjectId = ? WHERE (ObjectId = ?)
bind => [null, 24]
Fine: DELETE FROM TECPRHTABidParticipationItem WHERE (Id = ?)
bind => [24]
Finer: end unit of work flush
Finer: resume unit of work
Finer: begin unit of work commit
Finer: commit transaction
My problem is that the UPDATE statement over the table TECAttachment updates all records with the given Id and not only those related to the Entity TECPRHTABidParticipationItem.
I think I must override the sql statements DeleteObjectQuery or DataModifyQuery but don't know how.
Any help will be really appreciated.
I'm working with eclipselink-2.7.4
Thanks in advanace!
After a lot or searching, digging and reading I managed to solve my problem.
Basically:
My attachments collection is an unidirectional custom #OneToMany relationship.
I was in the correct path trying to achieve my goal with a collection customizer (a class associated to my collection via annotation and implementing the DescriptorCustomizer interface).
My customizer not only needs to customize the way the "child" records are selected, also it's needed to customize what to do when the parent record is deleted.
The way to do this is to override the default removeAllTargetsQuery property providing a new custom query through the method mapping.setCustomRemoveAllTargetsQuery(DataModifyQuery).
The most difficult part was to understand how the underlying eclipselink implementation sends parameters (which, order, type, etc.) to the custom DataModifyQuery. I had to download the source code of EclipseLink's JPA implementation and figure out how things are done...
Finally, everything is working good and ok thanks to the following simple DescriptorCustomizer:
package com.tec.uportal.model.customizer;
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.OneToManyMapping;
import org.eclipse.persistence.queries.DataModifyQuery;
/**
*
* #author MarcB
*/
public class AttachmenstCollectionAttributeCustomizer implements DescriptorCustomizer {
public static final String ATTACHMENTS__COLLECTION_NAME = "attachmentsCollection";
public static final String ATTACHMENTS_TABLE_OBJECT_TABLE__FIELD_NAME = "ObjectTable";
// Customize attachments collection mapping
#Override
public void customize(ClassDescriptor descriptor) {
// Customize how records are selected inside parent entity's attachments collection: include in the WHERE clause the column ObjectTable
OneToManyMapping mapping = (OneToManyMapping)descriptor.getMappingForAttributeName(ATTACHMENTS__COLLECTION_NAME);
ExpressionBuilder eb = new ExpressionBuilder();
Expression eObjectId = eb.getField(mapping.getTargetForeignKeyFields().get(0).getQualifiedName()).equal(eb.getParameter(descriptor.getPrimaryKeyFields().get(0)));
Expression eObjectTable = eb.getField(mapping.getTargetForeignKeyFields().get(0).getTable().getQualifiedName() + "." + ATTACHMENTS_TABLE_OBJECT_TABLE__FIELD_NAME).equalsIgnoreCase(descriptor.getTable(descriptor.getTableName()).getQualifiedName());
mapping.setSelectionCriteria(eObjectId.and(eObjectTable));
// Customize what must be done (delete childs) when parent entity is deleted
DataModifyQuery dmf = new DataModifyQuery("DELETE " + mapping.getTargetForeignKeyFields().get(0).getTable().getQualifiedName() + " WHERE " + mapping.getTargetForeignKeyFields().get(0).getName() + " = #" + mapping.getTargetForeignKeyFields().get(0).getName() + " AND " + ATTACHMENTS_TABLE_OBJECT_TABLE__FIELD_NAME + " = '" + descriptor.getTableName() + "'");
mapping.setCustomRemoveAllTargetsQuery(dmf);
}
}

JPA Criteria API join on 3 tables and some null elements

I have one parent entity that has two child entities as attributes.
I want to select all elements from the parent entity that have EITHER a childOne with a given parameter as personal attribute OR childTwo with that same given parameter as personal attribute.
Here are my three classes simplified:
The Parent Object:
#Entity
public class ParentObject {
#Id
private int id;
private int fkChildOne;
private int fkChildTwo;
#ManyToOne
#JoinColumn(name = "fk_child_one_id", referencedColumnName =
"child_one_id")
private ChildOne childOne;
#ManyToOne
#JoinColumn(name = "fk_child_one_id", referencedColumnName =
"child_one_id")
private ChildTwo childTwo;
// getters and setters
}
The Child One Object:
#Entity
public class ChildOne {
#Id
private int childOneId;
private String nameChildOne;
#OneToMany
#JoinColumn(name = "fk_child_one_id")
private List<ParentObject> parents;
// getters and setters
}
The Child Two Object:
#Entity
public class ChildTwo {
#Id
private int childOneId;
private String nameChildTwo;
#OneToMany
#JoinColumn(name = "fk_child_two_id")
private List<ParentObject> parents;
// getters and setters
}
The Specs Class:
public static Specification<ParentObject> checkName(String name) {
return Specifications.where(
(root, query, builder) -> {
final Join<ParentObject, ChildOne> joinchildOne =
root.join("childOne");
final Join<ParentObject, ChildTwo > joinchildTwo =
root.join("childTwo");
return builder.or(
builder.equal(joinchildOne .get("nameChildOne"), name),
builder.equal(joinchildTwo .get("nameChildTwo"), name)
);
}
);
}
When this spec is called in my service, I get no results. However, if I comment out one of the two joins and the corresponding Predicate in my builder.or method, then I get some results but they obviously don't match what I'm looking for, which is to select every ParentObject that have either ChildOne with that parameter or ChildTwo with that paramater.
Any clue what's wrong with the code ?
Finally got the solution : to fetch all the corresponding results, I had to add the type of the join which would be left join, since I wanted to fetch all ParentObjects regardless of owning childOne or ChildTwo objects.
final Join<ParentObject, ChildOne> joinchildOne =
root.join("childOne", JoinType.LEFT);
final Join<ParentObject, ChildTwo > joinchildTwo =
root.join("childTwo", JoinType.LEFT);
Great, now you have to choose if you need to join or fetch.To optimize the query and the memory, you should establish the relations as Lazy (#ManyToMany (fetch = FetchType.LAZY)), so you will only bring the objects that you demand.
The main difference is that Join defines the crossing of tables in a variable and allows you to use it, to extract certain fields in the select clause, for example, on the other hand, fetch makes it feed all the objects of that property. On your example,
a select from parent with join of children (once the relation is set to lazy) would only bring initialized objects of type parent, however if you perform a fetch, it would bring the parent and child objects initialized.
Another modification I would make is to change the type of the identifier to non-primitive, so that it accepts null values, necessary for insertion using sequences

Hibernate Filter being ignored

My application uses Hibernate 5.02 and Wildfly 10 with a PostgreSQL 9.5 database. I'm trying to enable a filter on a #OneToMany collection held within an entity that is constructed via a NamedQuery. Unfortunately, it seems as if the filter is just ignored. Here are the different components, redacted for ease of reading.
#NamedNativeQueries({
#NamedNativeQuery(
name = "getAnalystProcess",
query = "SELECT * FROM analysis.analystprocess WHERE id = :processId",
resultClass = AnalystProcessEntity.class
)})
#FilterDef(
name = "analystProcessUnanalyzedMsgsFilter",
parameters = { #ParamDef(name = "processIds", type = "integer"), #ParamDef(name = "analystIds", type = "integer") })
#Filter(name = "analystProcessUnanalyzedMsgsFilter", condition = "analystprocess_id IN (:processIds) AND id NOT IN (SELECT msg_id FROM analysis.analyzedmsg WHERE analyst_id IN (:analystIds) AND analystprocess_id IN (:processIds)) ORDER BY process_msg_id")
#Entity
#Table(name = "analystprocess", schema = "analyst")
public class AnalystProcessEntity implements JPAEntity {
public static final String GET_PROCESS = "getAnalystProcess";
public static final String MSG_FILTER = "analystProcessUnanalyzedMsgsFilter";
public static final String MSG_FILTER_PROC_ID_PARAM = "processIds";
public static final String MSG_FILTER_ANALYST_ID_PARAM = "analystIds";
private static final long serialVersionUID = 1L;
...
#OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "process")
#OrderColumn(name = "process_msg_id")
#LazyCollection(LazyCollectionOption.EXTRA)
private List<MsgEntity> msgList;
#Entity
#Table(name = "msg", schema = "analyst")
public class MsgEntity implements JPAEntity {
...
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "analystprocess_id", referencedColumnName = "id")
private AnalystProcessEntity process;
#Column(name = "process_msg_id")
private Integer processMsgId;
private void buildAnalystProcess() {
LOG.info("Building AnalystProcessEntity");
analystUser.getJdbcSession().enableFilter(AnalystProcessEntity.MSG_FILTER)
.setParameter(AnalystProcessEntity.MSG_FILTER_PROC_ID_PARAM, analystProcessId)
.setParameter(AnalystProcessEntity.MSG_FILTER_ANALYST_ID_PARAM, analystUser.getId());
Query query = analystUser.getJdbcSession().getNamedQuery(AnalystProcessEntity.GET_PROCESS)
.setParameter("processId", analystProcessId);
// Query query = analystUser.getJdbcSession().createNativeQuery("SELECT * FROM analysis.analystprocess WHERE id = :processId")
// .setParameter("processId", analystProcessId)
// .addEntity(AnalystProcessEntity.class);
analystProcess = (AnalystProcessEntity) query.getSingleResult();
CREATE TABLE analysis.analystprocess (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
description TEXT,
created_date TIMESTAMP NOT NULL DEFAULT now(),
...
);
CREATE TABLE analysis.msg (
id SERIAL PRIMARY KEY,
analystprocess_id INTEGER NOT NULL REFERENCES analysis.analystprocess(id) ON DELETE CASCADE ON UPDATE CASCADE,
process_msg_id INTEGER NOT NULL,
constraint tbl_statusid_analystprocessid unique(status_id, analystprocess_id)
);
As seen above, I have also tried the filter on constructing the AnalystProcessEntity class via createNativeQuery instead of getNamedQuery and no luck.
I also added a defaultCondition with hardcoded values into the #FilterDef just to see if it would execute the default condition and it still didn't.
I've tried the #Filter above the entity definition as well as above the class definition. I even came across a blog post which made it sound like the condition references entity fields (variable names) and not table fields (column names). Trying to stick to Java naming conventions in the Entity and Postgres naming conventions in the table, so I tried switching the references in the condition and to no avail.
I have sql logging turned on in Hibernate and the condition doesn't show up anywhere, as if it's just simply being ignored.
Any help would be greatly appreciated!
So, the problem was that I had the #FilterDef applied to the wrong class. It was my presumption that because I was constructing the AnalystProcessEntity which holds the MsgEntity collection (which I am trying to filter), that the #FilterDef would be applied to the AnalystProcessEntity class. Instead, it needs to be applied to the entity that it's actually filtering (hindsight being 20/20, that's pretty obvious).
Also, the actual condition needed to be modified to use complete references within the sub-select query.
I hope this helps someone at some point...
#NamedNativeQueries({
#NamedNativeQuery(
name = "getAnalystProcess",
query = "SELECT * FROM analysis.analystprocess WHERE id = :processId",
resultClass = AnalystProcessEntity.class
)})
#Filter(name = "analystProcessUnanalyzedMsgsFilter", condition = "id NOT IN (SELECT amsg.msg_id FROM analysis.analyzedmsg amsg WHERE amsg.analyst_id IN (:analystIds) AND amsg.analystprocess_id IN (:processIds))")
#Entity
#Table(name = "analystprocess", schema = "analyst")
public class AnalystProcessEntity implements JPAEntity {
public static final String GET_PROCESS = "getAnalystProcess";
public static final String MSG_FILTER = "analystProcessUnanalyzedMsgsFilter";
public static final String MSG_FILTER_PROC_ID_PARAM = "processIds";
public static final String MSG_FILTER_ANALYST_ID_PARAM = "analystIds";
private static final long serialVersionUID = 1L;
...
#OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "process")
#OrderColumn(name = "process_msg_id")
#LazyCollection(LazyCollectionOption.EXTRA)
private List<MsgEntity> msgList;
#FilterDef(
name = "analystProcessUnanalyzedMsgsFilter",
parameters = { #ParamDef(name = "processIds", type = "integer"), #ParamDef(name = "analystIds", type = "integer") })
#Entity
#Table(name = "msg", schema = "analyst")
public class MsgEntity implements JPAEntity {
...
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "analystprocess_id", referencedColumnName = "id")
private AnalystProcessEntity process;
#Column(name = "process_msg_id")
private Integer processMsgId;
Additionally, I ran into another problem with null's appearing in the collection, despite the fact that I am using an #OrderColumn, which I thought fixed that issue. It seems that with the use of the #Filter, null's are inserted in place of what ended up being filtered OUT (excluded).

Jpa eclipselink bug when try persist collection by another entity

Hello i have got one entity which contains collection of object:
#Entity
public class ResourceType{
#Id
public Integer id;
#ManyToMany(mappedBy = "resourcesTypes")
private Set<Resource> resources = new HashSet<>();
}
As you see this entity contains collection of Resource
#Entity
public class Resource{
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "RESOURCE_TYPE_CROSS", joinColumns = { #JoinColumn(name = "RESOURCE_ID") }, inverseJoinColumns = { #JoinColumn(name = "RESOURCE_TYPE_ID", referencedColumnName = "ID") })
private List<ResourceType> resourcesTypes = new ArrayList<>();
}
So i guess if i get all resourceTypes with all resources this collection is in local cache.
But if i try persist resource :
#Override
public ResourcesDTO createResource(ResourcesDTO resource) throws CreateResourceException {
if (resource.getResourcesTypes().isEmpty()) {
throw new CreateResourceException("Can not create resouce " + resource.getName()
+ " none resource types is set");
resourceDAO.create(map(resource));
log.debug("Created resource: " + r);
resource.setId(r.getId());
return resource
}
If i perssist Resource with ResourcesTypes and next i will get all ResourceTypes entity manager do not found new Resources. But in my cross table resource_type_cross everything is ok. i will try to do something like that after create new resource:
for(ResourceType rt : resource.getResourceTypes()){
em.refresh(rt);
}
But its not working properly. After i will restart server everything is fine. But why entity manager do not refresh resource type ??
This is what i use to read all ResourceTypes:
public ResourceType getAllResourceTypes(){
em.createQuery("Select n from Resource n left join fetch n.children");
return em.find(ResourceType.class, 0); //0 - ROOT
}
as follow article : http://www.tikalk.com/java/load-tree-jpa-and-hibernate
The Main question is that :
create or update entity by one side do not update another, so in my case i create and update resource type by resource entity, but i am using resouce type to get all resource.
First, there is a problem with your ManyToMany mappings. You have made both sides unidirectional accessing the same join table instead of bidirectional. One side should control the mapping, with the other set to mappedby the owning side.
Second, have you flushed or committed the changes when you refresh ResourceTypes? It is probably better to maintain both sides by setting the reference in code rather than query to refresh each and every ResourceType