Menu Tree with Hibernate in Play! - java

My first project with Hibernate/JPA and Play!, I've got a menu that once working, will support changes (i.e. easily add nodes to the tree). Struggling (in the >5 hours sense) just to get the basic modelling together:
The model:
#Entity
#Table(name = "Node")
public class Node extends Model {
#Column(nullable=false)
public String description;
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="parent")
public List<Node> children = new LinkedList<Node>();
#ManyToOne
#JoinColumn(name="parent", insertable=false, updatable=false)
public Node parent;
public Node(){}
}
The util class:
public class NodeUtil {
public static void addChild(Node root, String description) {
Node child = new Node();
child.description = description;
child.parent = root;
root.children.add(child);
root.save();
}
private static final String MENU_NAME = "MainMenu";
public static Node getMenu() {
return getRoot(MENU_NAME);
}
public static Node getRoot(String name) {
Node root = Node.find("byDescription", name).first();
if (root == null) {
root = new Node();
root.description = name;
root.save();
}
return root;
}
}
The test:
public class MenuTest extends UnitTest {
private static final String TEST_MENU = "testMenu";
#Test
public void testMenu() {
// test build/get
Node root = NodeUtil.getRoot(TEST_MENU);
assertNotNull(root);
// delete all children - maybe from previous tests etc.
for(Node o : root.children)
o.delete();
root.save();
// test add
root = NodeUtil.getRoot(TEST_MENU);
NodeUtil.addChild(root, "child 1");
NodeUtil.addChild(root, "child 2");
NodeUtil.addChild(root, "child 3");
assertEquals(3, root.children.size());
assertEquals("child 3", root.children.get(2).description);
assertEquals(0, root.children.get(1).children.size());
Node node = root.children.get(1);
NodeUtil.addChild(node, "subchild 1");
NodeUtil.addChild(node, "subchild 2");
NodeUtil.addChild(node, "subchild 3");
NodeUtil.addChild(node, "subchild 4");
NodeUtil.addChild(root.children.get(2), "sub subchild");
assertEquals("sub subchild", root
.children.get(1)
.children.get(2)
.children.get(0)
.description);
assertEquals(4, root.children.get(1).children.size());
root.save();
// test delete
root = NodeUtil.getRoot(TEST_MENU); // regrab the root via hibernate, assuming there isnt it isnt cached this will get changes that have been persisted to the db (maybe?)
root.children.get(1).children.get(2).delete();
assertEquals(3, root.children.get(1).children.size());
//root.delete();
}
}
Questions:
What am I doing wrong? (I.e. I just can't get this simple idea to be modelled and to pass the unit test. Like I said, new to Hibernate, and every change I make yields a new Hibernate error variant, which means nothing to me. E.g. this current setup throws "detached entity passed to persist: models.Node")
Initially I had the entire util class as a bunch of static methods in the model class. Firstly, do static methods affect Hibernates modelling? If so, in brief, under what circumstances can I have static methods (and members, come to think of it) that will be "transient" to the object modelling?
Assuming that I keep the util methods in a separate public util class, where is this class conventionally stored in the play framework? At the moment it's in the models package, next to the Node.java.

I'm not familiar with Play Framework, but I can make some point regarding working with Hibernate:
Maintaining consistent state of the objects in memory is your responsibility. Consider the following code:
for(Node o : root.children)
o.delete();
root.save();
You instructed Hibernate to delete children from the database, but the root object in memory still references them. Since the relationship is configured with cascading, Hibernate will try to save them again (I guess it's the reason of "detached entity passed to persist" error). So, keep in-memory state of the object consistent by clearing root.children.
Hibernate heavily relies on the concept of Unit of Work. I'm not sure how Play manages it, but it looks like you should call clearJPASession() in unittests to make sure that exisiting session state wouldn't affect further operations:
root.save();
clearJPASession();
// test delete
root = NodeUtil.getRoot(TEST_MENU);
The way you defined a relationship is supported, but not recommended (see 2.2.5.3.1.1. Bidirectional). Use the following approach instead:
#OneToMany(mappedBy = "parent", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
public List<Node> children = new LinkedList<Node>();
#ManyToOne
#JoinColumn(name="parent")
public Node parent;
Static methods doesn't interfere with Hibernate.

Related

Propagate and persist changes in a tree in Spring JPA/Boot

I am struggeling with a design - persistence issue . I have a tree and all elements in this tree are subclasses of the same abstract superclass. The tree has a root object with a list of children which themselves can have children, and so on. Changes to attributes of a children would need to be propagated to all parent levels. To make it easier to illustrate, let's say we deal with products which would have multiple releases, having multiple development phases. Each of these would have a lifetime. So we would have something like
#Entity
#Access(AccessType.PROPERTY) // JPA reading and writing attributes through their getter and setter methods
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
discriminatorType = DiscriminatorType.STRING,
name = "lifetime_level",
length=10
)
public abstract class LifeTime implements Serializable {
// all classes are incomplete for sake of brevity
protected Integer version; // Optimistic locking support
protected LocalDate plannedBegin; // The calculated, potentially shifted, begin resulting from children assignments
protected LocalDate plannedEnd;
private LifeTime parent;
protected List<LifeTime> children;
#ManyToOne
public LifeTime getParent() {
return parent;
}
public void setParent(LifeTime parent) {
this.parent = parent;
}
#OneToMany(mappedBy="parent")
public List<LifeTime> getChildren() {
return children;
}
private void setChildren(List<LifeTime> children) {
this.children = FXCollections.observableList(children);
}
public void addChild(LifeTime child) {
children.add(child);
}
public LocalDate getPlannedBegin() {
return plannedBegin;
}
public void setPlannedBegin(LocalDate aBegin) {
this.plannedBegin = aBegin;
adjustParentBegin(parent, this);
}
public LocalDate getPlannedEnd() {
return plannedEnd;
}
public void setPlannedEnd(LocalDate aEnd) {
this.plannedEnd = aEnd;
adjustParentEnd(parent, this);
}
protected void adjustParentBegin(LifeTime parent, LifeTime child) {
if(child.getPlannedBegin.isBefore(parent.getPlannedBegin)) {
parent.setPlannedBegin(child.getPlannedBegin);
}
}
protected void adjustParentEnd(LifeTime parent, LifeTime child) {
if(child.getPlannedEnd.isAfter(parent.getPlannedEnd)) {
parent.setPlannedEnd(child.getPlannedEnd);
}
}
#Version
public Integer getVersion() {
return version;
}
private void setVersion(Integer version) {
this.version = version;
}
}
We would have the concrete classes Product, Release and Phase. All would extend LifeTime. As root object we have a product. The product has children representing releases of the product and each release would have several development phases. We would also have baselines, iterations that we ignore for the moment. Next, we have somewhere a service class that cares for handling the business logic:
#Service
public class LifeTimeService {
#Autowired ProductRepository productRepository;
#Autowired ReleaseRepository releaseRepository;
#Autowired PhaseRepository phaseRepository;
public Product createProduct() {
Product product = new Product();
release.setPlannedBegin(LocalDate.now());
release.setPlannedEnd(LocalDate.now());
product = productRepository.save(product); // without will throw TransientPropertyValueException: object references an unsaved transient instance when saving release
createRelease(product);
return productRepository.save(product);
}
public Release createRelease(Product product) {
Release release = new Release();
product.addChild(release);
release.setParent(product);
release.setPlannedBegin(product.getPlannedBegin()); // initial values
release.setPlannedEnd(product.getPlannedEnd());
release = releaseRepository.save(release); // without will throw TransientPropertyValueException: object references an unsaved transient instance when saving phases
addPhases(release); // add phases and adjust begin and end of the release
return releaseRepository.save(release);
}
protected void addPhases(Release release) {
LocalDate date = release.getPlannedBegin;
Phase phase1 = new Phase();
release.addChild(phase1);
phase1.setParent(release);
phase1.setPlannedBegin(date);
date = date.plusMonth(3);
phase1.setPlannedEnd(date);
phaseRepository.save(phase1);
phase2 = new Phase();
release.addChild(phase2);
phase2.setParent(release);
phase2.setPlannedBegin(date);
date = date.plusMonth(3);
phase2.setPlannedEnd(date);
phaseRepository.save(phase2);
}
}
Let's say we have Controller class, that makes use of the Service
#Controller
public class MyController {
#Autowired LifeTimeService service;
protected Product product;
public void myTest() {
Product product = service.createProduct(); // this creates a product with an initial release and all phases
Release release = service.createRelease(product); // now the product has a later plannedEnd than the corresponding database row
}
}
Obviously, I want that the createRelease method creates and returns a release. The issue is that it alters all levels of the tree: It creates phases but also changes the parent product begin and end date. I would need to save the product after I call createRelease to persist this change. This approach doesn't scale if the tree has more levels. Otherwise, if I save the product within createRelease, the product in the myTest method would have the wrong version. Or createRelease returns the saved parent - what is counter intuitive - and I have to code a method which return last created release. This is all unsatisfying.
While the above class example follows the Domain Driven Design, whereby each object is in charge of securing its integrity, I was although thinking about Anemic Domain Model and moving the two adjustment methods to the service class and make them recursive. But I don't see how it changes or fixes the above issue.
In reality my tree goes over at least 5 hierarchical levels. So whatever solution you propose, it should scale to multiple levels.

RelationshipEntity not persisted

I'm having problems with relation
#RelationshipEntity(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION)
public class TagOnObjectEvaluation
{
#StartNode
private Mashup taggableObject;
#EndNode
private Tag tag;
// Other fields, getters and setters
}
In both the entities involved (Mashup and Tag), I have this field (with opposite Direction)
#RelatedToVia(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION,
direction = Direction.INCOMING /*Direction.OUTGOING*/)
private Set<TagOnObjectEvaluation> tagOnObjectEvaluations =
new HashSet<TagOnObjectEvaluation>();
Then, I have various service class to manage Tag, Mashup and TagOnObjectEvaluation. The class under test now is the latter.
Note: the name is a bit confusing and it's a legacy from the previous coder, you can read DAO as Service. Also GenericNeo4jDAOImpl (again, read it as GenericServiceNeo4jImpl) simply defines standard methods for entities management (create(), find(), update(), delete(), fetch() )
#Service
public class TagOnObjectEvaluationDAONeo4jImpl extends
GenericNeo4jDAOImpl<TagOnObjectEvaluation> implements
TagOnObjectEvaluationDAO
{
#Autowired
private TagOnObjectEvaluationRepository repository;
public TagOnObjectEvaluationDAONeo4jImpl()
{
super(TagOnObjectEvaluation.class);
}
public TagOnObjectEvaluationDAONeo4jImpl(
Class<? extends TagOnObjectEvaluation> entityClass)
{
super(entityClass);
}
#Override
public TagOnObjectEvaluation create(TagOnObjectEvaluation t)
{
Transaction tx = template.getGraphDatabaseService().beginTx();
TagOnObjectEvaluation savedT = null;
try
{
// This is to enforce the uniqueness of the relationship. I know it can fail in many ways, but this is not a problem ATM
savedT =
template.getRelationshipBetween(
t.getTaggableObject(), t.getTag(),
TagOnObjectEvaluation.class,
RelTypes.Tag.TAG_ON_OBJECT_EVALUATION);
if (savedT == null)
savedT = super.create(t);
tx.success();
}
catch (Exception e)
{
tx.failure();
savedT = null;
}
finally
{
tx.finish();
}
return savedT;
}
}
It seems pretty straightforward until now.
But when I'm trying to persist a RelationshipEntity instance, I have many problems.
#Test
public void testRelationshipEntityWasPersisted()
{
TagOnObjectEvaluation tagOnObjectEvaluation = new TagOnObjectEvaluation(taggedObject, tag);
tagOnObjectEvaluationDao.create(tagOnObjectEvaluation);
assertNotNull(tagOnObjectEvaluation.getId());
LOGGER.info("TagOnObjectEvaluation id = " + tagOnObjectEvaluation.getId());
tagDao.fetch(tag);
assertEquals(1, tag.getTaggedObjectsEvaluations().size());
}
The last test fail: the size is 0 and not 1. Also, although it seems that the entity is correctly stored (it gets an id assigned), if I'm navigating the db later on there is no track of it at all.
I've also tried to add the relationship in a different way, using the sets of the involved nodes; f.e.
tag.getTaggedObjectsEvaluations().add(tagOnObjectEvaluation);
tagDao.update(tag);
but with no improvements at all.
You need to change the direction of the relationship in your entity Mashape, (entity corresponding to the #StartNode of your #RelationshipEntity TagOnObjectEvaluation).
#NodeEntity
class Mashape {
// ...
#RelatedToVia(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION, direction = Direction.OUTGOING)
private Set<TagOnObjectEvaluation> tagOnObjectEvaluations = new HashSet<TagOnObjectEvaluation>();
}
Just point that according to the specifications of #RelatedToVia spring-data-neo4j annotation, the direction by default is OUTGOING, so you really don't need to specify the direction in this case. This also should be correct:
#RelatedToVia(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION)
private Set<TagOnObjectEvaluation> tagOnObjectEvaluations = new HashSet<TagOnObjectEvaluation>();
Hope it helps.

Hide Hibernate session handling from business logic

Suppose I have an entity "Parent" which holds a list of "Child" objects.
In Java this looks like this:
public class ParentEntity implements Parent {
protected int id;
#Override
public int getId() { return id; }
#Override
public void setId(int id) { this.id = id; }
protected List<Child> children;
#Override
public List<Child> getChildren() { return children; }
#Override
public void setChildren(List<Child> children) { this.children = children; }
#Override
public void save() {
// Do some Hibernate "save" magic here...
}
public static Parent getById(int id) {
Session session = HibernateUtil.getSessionFactory().openSession();
Parent entity = (Parent) session.get(ParentEntity.class, id);
session.close();
return entity;
}
}
My business logic class shall only work with the interface class, like this:
public class BusinessLogic {
public void doSomething() {
Parent parent = ParentEntity.getById(1);
for (Child c : parent.getChildren())
System.out.println("I love my daddy.");
}
}
Unfortunately, this doesn't work because the parent's children do not get loaded and the loop crashes with a NullPointerException.
1. Approach "Eager Loading"
There are two problems with this approach. Even though in the XML I wrote "lazy='false'" Hibernate seems to ignore this.
Secondly, eager loading is not desirable in my case since we could have hundreds of children, potentially.
2. Approach "Load/Initialize on 'GET'"
#Override
public List<Child> getChildren()
{
if (!Hibernate.isInitialized(children)) {
Session session = HibernateUtil.getSessionFactory().openSession();
Hibernate.initialize(children);
session.close();
}
return children;
}
This doesn't work either because I get an exception saying that the collection is not linked to a session. The session which was used to load the parent entity was closed, previously.
What do you suggest is the 'best practice' solution here? I really don't want to mess with Hibernate sessions in my business logic.
Either you can use a query-object or a custom query and fetch all children in the scenarios you really need them (search for left join fetch), for a few thousand objects this might work.
However if the amount of records could reach millions you are running most likely into memory issues, then you should think about a shared cache, retrieving the data on a page by page basis Just search for n+1 and hibernate you will find plenty of discussions around this topic.
The simplest hack I can think of:
public static Parent getParentWithChildrenById(int id) {
Session session = HibernateUtil.getSessionFactory().openSession();
Parent entity = (Parent) session.get(ParentEntity.class, id);
Hibernate.initialize(entity.children);
session.close();
return entity;
}
Side-note: Having the data access logic in you domain layer is considered bad practice.
I've always allowed Spring and JPA to manage my Hibernate sessions, so most of the painful boilerplate code disappears at that point. But you still have to call entity.getChildren().size() (or something similar) before you exit the data layer call where the session is opened, to force Hibernate to do the retrieval while there's still a session to use.

One to many relationship

My questions i about storing one-to-many relationship to database (as Programming a one-to-many relationship)
Lets assume the example: we have parent and children. Each parent record can have many childs.
// Db storage:
// id name
public class Parent {
Set<Childs> mChilds;
private long mId;
private String mName;
// etc
public Parent(String name) {
this.mName = name;
}
public static Parent load(id) {
// query database for all Parent attributes
Parent p = new Parent("somename");
// now its time to load set of child (1)
Set<Child> children = Child.loadChildren(p);
}
}
// Db storage
// id name parent_ref
public class Child {
private Parent mParent;
private String mName;
public Child(String name, Parent parent) {
this.mName = name;
this.mParent = parent;
}
// loads a child with specific Id
public static Child load(long id) {
// query db like
Cursor c = db.query("child_table", new String[] {"id", "name", "parent_ref"}, "id = ?", new String[] {String.valueOf(id)});
// now we have a cursor, we can construct child!
// Oh my God! I call Parent.load, that calls loadChildren(3)
return new Child(c.getColumn("name"), Parent.load(c.getColumn("parent_ref")));
}
public static Set<Child> loadChildren(Parent parent){
// fetch all child ids, that given parent has
List<long> childIds = getAllIds(parent);
// for each id load child (2)
Child c = Child.load(childId);
}
}
As you can see, I'd like to load parent by given Id. Parent's load function call child's loadlChildren (call (1)), which calls Child.load(call (2)), which calls Parent.load (3) because it needs to make a reference to parent!
So there are two major questions as Im new to java dev.
1) Are there any workarounds about this problems? what Im doing wrong?
2) My call to loadChildren will create n Parents (n references to n objects) instead of creating references to one object. What should I do?
Why don't you implement some kind of cache system (maybe with a Java HashMap) where you can put a parent and its children and instead of loading them anyway, load them only if they aren't already in the cache? And get a reference to them if they are alredy in the cache.
I'd also have a look at the: Flyweight pattern
Or another solution I can think is to implement another method in the Parent class: somethink like "loadOnlyParent()" which loads only the Parent (if not already loaded) without the children. And implement a lazy loading of the children (if not already loaded) only when necessary.

Can Hibernate automatically manage the order of child collections?

Given a Parent class which has many Children, can Hibernate automatically manage the order of said children? From reading documentation and searching, it seems that the #OrderColumn annotation may enable this, but I've not found any examples of how to do it. The closest thing I've found is JPA 2.0 #OrderColumn annotation in Hibernate 3.5, which looks a bit discouraging, given that it looks just like what I want to do.
Here is a rough sketch of what I'm trying to do, minus the annotations, since I'm not sure what they would be:
class Parent {
// I probably need some sort of #OrderColumn annotation here, right?
private List<Child> children = new ArrayList<Child>;
}
class Child {
private Parent parent;
private int order;
}
class SomeBusinessLogic {
public static void createFamily() {
Parent dad = new Parent("Tom");
List<Children> children = dad.getChildren();
children.add(new Child("Alice");
children.add(new Child("Bob");
children.add(new Child("Carl");
session.save(dad);
}
public static void addChild(Parent parent) { // assuming Tom is passed in
List<Children> children = parent.getChildren();
children.add(1, new Child("Zelda");
session.save(parent);
// now, the order should be: Alice, Zelda, Bob, Carl
}
}
If someone can give me enough details to make this toy example work (or tell me that it will not work), I would be most appreciative.
In order to store children in its order you should map it with #IndexedColumn annotation it will create indexed column on your child objects table.

Categories