JPA OneToMany Cascade - java

we are developing an application in which we have a tree-structure. Using Java EE and JPA (EclipseLink) and MySQL.
My Problem:
Saving the parent causes saving all childs.
I removed the CascadeType PERSIST and MERGE but still it cascades.
Example Code:
Parent Node:
#Entity
#Table(name = "node")
#NamedQuery(name = "node.findAll", query = "SELECT n FROM Node n")
public class Node implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "NAME", nullable = false, unique = true)
private String name;
#OneToMany(fetch = FetchType.LAZY)
#MapKey(name = "name")
#MapKeyColumn(name = "name")
#MapKeyJoinColumn(name = "name")
#JoinColumn(name = "NODE_ID", referencedColumnName = "id")
private Map<String, Data> datas = new HashMap<String, Data>();
And the Child Node:
#Entity
#Table(name = "data")
#NamedQuery(name = "data.findAll", query = "SELECT d FROM Data d")
#IdClass(DataId.class)
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Id
#Column(name = "NAME", nullable = false)
private String name;
The parent node "Node" has also a parent called System. The code is similar to Node and Data. There are 8 Levels, but all are similar, using OneToMany relation to build the tree.
Is it possible to avoid saving the Data (Child) objects when im saving the Node (Parent). Or for example i want to save an object from level 6 (System Node) so it should not persist or merge the child nodes.
Because im receiving some Nodes and Datas from a Client per REST and i want to save the Node immidiately but the Data objects later using a thread which checks which Datas are new and save only them.

Related

Performing Full Text Search on Results of JPA Query Using Hibernate Search

I have 3 Entities:
public class Parent{
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Field(name ="name")
private String name;
#OneToMany(targetEntity = Child.class)
private Set<Child> children;
}
public class Child{
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(targetEntity = Parent.class)
private Parent parent;
#ManyToOne(targetEntity = GrandChild.class)
private GrandChild grandchild;
}
public class GrandChild{
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
}
I currently perform a full text search on "name" in parent using hibernate search. I would like to allow the user to supply GrandChild.id, use normal JPA query to get all Parents associated with Grandchild, and then perform a full text search on name using Hibernate Search.
Is this possible?
Unless you have very specific requirements, I wouldn't recommend mixing a JPA query and a full-text search query; this will complicate things and may lead to performance bottlenecks.
The thing is, it's absolutely possible to perform the whole query using Hibernate Search only, by adding two predicates to your full-text query: one on the name, and one on the grandchild ID.
Step 1: make sure that you include grandchildren (and their ID) in Parent using #IndexedEmbeded:
#Indexed // THIS IS NEW
public class Parent{
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Field(name ="name") // Removed "nullable" here, this attribute doesn't exist
private String name;
// Don't forget to add the missing "mappedBy" here
#OneToMany(targetEntity = Child.class, mappedBy = "parent")
#IndexedEmbedded // THIS IS NEW
private Set<Child> children;
}
public class Child{
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(targetEntity = Parent.class)
#ContainedIn // THIS IS NEW
private Parent parent;
#IndexedEmbedded(includePaths = "id") // THIS IS NEW
#ManyToOne(targetEntity = GrandChild.class)
private GrandChild grandchild;
}
public class GrandChild{
#Id
#Field // THIS IS NEW
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
// THIS IS NEW
#OneToMany(targetEntity = Child.class, mappedBy = "grandchild")
#ContainedIn
private Set<Child> parents = new HashSet<>();
}
Step 2: review the code that updates your entities. You must make sure that whenever you create or update an association on one side (e.g. Child.grandchild) you also update the other side (e.g. GrandChild.parents).
Step 3: reindex
FullTextSession fullTextSession = Search.getFullTextSession( session );
fullTextSession.createIndexer().startAndWait();
Step 4: query
// Input
int grandChildId = ...;
String terms = ...;
FullTextSession fullTextSession = Search.getFullTextSession( session );
QueryBuilder qb = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity( Parent.class ).get();
Query luceneQuery = qb.bool()
.must( qb.keyword().onField( "name" ).matching( terms ) )
.must( qb.keyword().onField( "children.grandchild.id" )
.matching( grandChildId ) )
.createQuery();
org.hibernate.Query query =
fullTextSession.createFullTextQuery( luceneQuery, Parent.class );
List<Parent> result = query.list();

Get recently updated results from Database when joining multiple tables using JPA in a Spring application

I am new to Spring and JPA and I am trying to write a job in Spring which runs every 3 hours and retrieve the records from Oracle Database.
I would like to only read the new/updated content from the past 3 hours (ideally from the last job run).
I have seen few examples in https://spring.io/blog/2011/02/10/getting-started-with-spring-data-jpa/ where we can create queries and retrieve the data based on our requirements, but in my current use case, I am not using queries instead using the java classes with the annotations and using Join columns between different tables. There are chances that only one of the sub table is updated or all the tables are updated with new content. I need to get the results if at least one of the table is updated/inserted with new content.
Campus is the main table and retrieves the data from Group and Region, I need to fetch the data if any new data is updated in Campus table or even any group/region is added/modified.
I am using JDK7 as of now.
Is there a way to accomplish the above requirement?
Below are the sample Java classes for reference.
#Entity
#EntityListeners(AuditListener.class)
#Table(name = "TABLE_CAMPUS")
public class Campus implements Auditable {
#Id
#Column(name = "ID)
#SequenceGenerator(name = "SIMPLE_ID", sequenceName = "SIMPLE_ID")
#GeneratedValue(generator = "SIMPLE_ID", strategy = GenerationType.AUTO)
private Long id;
#Column(name = "CAMPUS_NAME")
private String campusName;
#Column(name = "CAMPUS_ID", nullable = false)
private Long campusId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "GROUP_ID")
private GroupType groupType;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "REGION_ID")
private Region region;
....
...
}
#Entity
#EntityListeners(AuditListener.class)
#Table(name = "TABLE_GROUP_TYPE")
public class GroupType implements Auditable {
#Id
#Column(name = "GROUP_TYPE_ID")
#SequenceGenerator(name = "GROUP_TYPE_SEQUENCE", sequenceName = "GROUP_TYPE_ID")
#GeneratedValue(generator = "GROUP_TYPE_SEQUENCE", strategy = GenerationType.AUTO)
protected Long id;
#Column(name = "GROUP_TYPE_NAME", nullable = false)
protected String groupTypeName;
....
...
}
#Entity
#EntityListeners(AuditListener.class)
#Table(name = "TABLE_REGION")
public class Region implements Auditable {
#Id
#Column(name = "region_id")
#SequenceGenerator(name = "REGION_ID", sequenceName = "REGION_ID")
#GeneratedValue(generator = "REGION_ID", strategy = GenerationType.AUTO)
private Long id;
#Column(name = "REGION_NAME", nullable = false)
private String name;
...
..
}
Any help is Appreciated.

How to persist objects and its nested objects at once?

I'm with some problems trying to persist an object and its items, here're my classes:
#Entity(name = "Contract")
#Table(name = "contract")
public class Contract implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private Long id;
#OneToMany(mappedBy = "idContract", cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private List<ContractItem> contractItem;
//getters & setters...
}
.
#Entity(name = "ContractItem")
#Table( name = "contract_item")
public class ContractItem implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private Long id;
#Column(name = "id_contract")
private Long idContract;
//getters & setters...
}
I'm extending JpaRepository im my repositories and using .save(contract) to persist but every time my application only persists the contract not de items, I've already tried CascadeType.ALL, MERGE and PERSIST in which either the result is the same, or I get an exception that my idContract must not be null.
Need some help here guys, thanks in advance !

How remove some fields of referenced entity from criteria query in hibernate

Suppose we have entity A that contains a list of entities with type B (with lazy initialization).
Entity B has one BLOB field and some other, that doesn't contain much data.
How can I, using hibernate criteria query, get entity A with it's fields and each A-entity with list of Bs, but every B-entity without the BLOB field ?
Also, I do not want to extract As and iterate them to get Bs. (I now, how to use 'Projections' to extract just Bs with required fields).
Here is some code sample:
A-entity:
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
#Cascade(CascadeType.ALL)
private List<B> list = new LinkedList<>();
}
B-entity:
#Entity
#Table(name = "B")
public class B implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "data", nullable = false)
#Lob
private byte[] data;
#ManyToOne(targetEntity = A.class, fetch = FetchType.EAGER)
#JoinColumn(name = "A_id", nullable = false)
private A a;
}

How to properly delete a complex object using Hibernate?

Here is the problem. When I'm trying to delete a Catalog object from database, Hibernate also removing all Catalog objects with associated Type and Genre Ids. For example, if I’m removing Catalog with Type.id=1 and Genre.id=1 Hibernate delete every Catalogs with such Ids. Any ideas how to fix it? I need to delete only one Catalog object without deleting Type and Genre objects with id=1.
#Entity
#Table(name = "catalog", catalog = "media_store_db")
public class Catalog implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "product_name", length = 100)
private String productName;
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "genre_id", referencedColumnName = "genre_id")
private Genre genre;
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "type_id", referencedColumnName = "type_id")
private Type type;
#Entity
#Table(name = "genres", catalog = "media_store_db")
public class Genre implements Serializable {
#Id
#Column(name = "genre_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "genre_name")
private String name;
#OneToMany(mappedBy = "genre", cascade = CascadeType.ALL)
Collection<Catalog> catalogs = new ArrayList<Catalog>();
#Entity
#Table(name = "types", catalog = "media_store_db")
public class Type implements Serializable {
#Id
#Column(name = "type_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "type_name")
private String name;
#OneToMany(mappedBy = "type", cascade = CascadeType.ALL)
Collection<Catalog> catalogs = new ArrayList<Catalog>();
My method which delete a Catalog object
public void deleteCatalog(Integer catalogId) {
Session session = config.getSession();
Transaction tx = session.beginTransaction();
session.delete(session.get(Catalog.class, catalogId));
tx.commit();
session.close();
}
This is because of Cascade.ALL. If you delete a parent if would also delete all related child if you are using Cascade.ALL.
Instead ALL choose only what you need from the below
CascadeType.PERSIST: cascades the persist (create) operation to associated entities if persist() is called or if the entity is managed
CascadeType.MERGE: cascades the merge operation to associated entities if merge() is called or if the entity is managed
CascadeType.REMOVE: cascades the remove operation to associated entities if delete() is called
CascadeType.REFRESH: cascades the refresh operation to associated entities if refresh() is called
CascadeType.DETACH: cascades the detach operation to associated entities if detach() is called
CascadeType.ALL: all of the above

Categories