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.
Related
Using Jackson with Retrofit, I wanted to have list of friends set to friendToMany during Deserialization. As I have gone through with documentation, we have to manually assign that entity to boxstore when assignable=true is set. So, I am doing this way(as shown in code). This approach works only for first item which this code is part of. It doesn't work for element 2 or further.
#Id(assignable = true)
#JsonProperty("_id")
public long id;
#Transient
private List<Friend> friends = null;
#JsonIgnore
#Backlink(to = "demoResponseToOne")
ToMany<Friend> friendToMany;
#JsonProperty("friends")
public void setFriends(
List<Friend> friends)
{
this.friends = friends;
for (Friend friend : friends)
{
MyApplication.getBoxStore().boxFor(Friend.class).attach(friend);
friendToMany.add(friend);
}
}
Exception thrown is : io.objectbox.exception.DbDetachedException: Cannot resolve relation for detached entities, call box.attach(entity) beforehand. at the time of add(friend). I mean this works when this Root element is first item of list.
You need to attach the Box<Friend> to this as well, which owns the ToMany to be modified:
MyApplication.getBoxStore().boxFor(Friend.class).attach(this);
Background: If you are using #Id(assignable = true) you need to take care of some things that ObjectBox would normally do for you. This includes attaching the Box before modifying any ToMany.
Source:
https://docs.objectbox.io/relations#updating-tomany
https://docs.objectbox.io/advanced/object-ids#self-assigned-object-ids
Background
I have encountered many methods in code I am tasked with refactoring that follow a general pattern as follows:
Take a composite input
Find children of composite according to some criterion
Remove children from composite, but keep track of them
Return the updated composite and append the removed children to an output parameter collection
In code it looks like this:
public Composite trim(Composite composite, List<Child> removed){
for (Child child : composite.getChildren()){
if (criterion(child)){
composite.remove(child);
removed.add(child);
}
}
}
There's a number of code smells here, but what I would like to address is how I can refactor this into immutable code that, accordingly, doesn't write to an output parameter.
Possible, but not-so-elegant solution
public Map<String, Object> trim(Composite composite){
final List<Child> removableChildren = filter(composite);
final Composite trimmed = copyCompositeWithoutChildren(composite, removableChildren);
return wrapInMap(removableChildren, trimmed);
}
Question
Are there more succinct ways of doing this using, for instance, functional programming approaches like collect or splice in groovy or Java 8 that might inform a better, albeit more verbose, approach in Java pre-8? Examples from either of the languages would be highly appreciated.
Edit:
2nd possible solution
Inspired by Steinar's answer: Use strategy pattern.
public interface FilterStrategy {
List<Child> filter(List<Child> children);
}
public interface RemovalResponseStrategy {
void respond(List<Child> removedChildren);
}
public class CompositeTrimmer {
private final FilterStrategy filterStrategy;
private final RemovalResponseStrategy removalResponseStrategy;
public Composite trim(final Composite composite){
final List<Child> removableChildren =
filterStrategy.filter(composite.getChildren());
final Composite trimmed =
copyCompositeWithoutChildren(composite, removableChildren);
removalResponseStrategy.respond(removableChildren);
return trimmed;
}
}
You didn't say what kind of freedom you have to change the Composite class or which methods are
already available. So I just made something up. It can probably be refitted to what you have available
quite easily.
A little bit more groovy solution would be something like this:
def trim(Composite composite, Closure removeCriterion) {
List<Child> removedChildren = composite.children.findAll(removeCriterion)
List<Child> remainingChildren = composite.children - removedChildren
Composite filteredComposite = new Composite(children: remainingChildren)
[filteredComposite, removedChildren]
}
You don't need to specify the criterion as a parameter, but I kind of like it. Makes the code more
flexible.
So basically, first find children to remove, then create a new composite with the remaining children.
Finally return a list of both outputs. Since groovy supports multiple assignments from a list, that's
an easy way to return several outputs.
The solution was tested with a dummy implementation of Composite and Child.
Usage and test:
def test() {
Composite composite = new Composite(children: [
new Child(name: 'Angus'),
new Child(name: 'Steinar'),
new Child(name: 'Erik'),
new Child(name: 'William'),
])
def (Composite filteredComposite, List<Child> removedChildren) =
trim(composite) {
Child child -> child.name > 'H'
}
assert filteredComposite.children == [new Child(name: 'Angus'), new Child(name: 'Erik')]
assert removedChildren == [new Child(name: 'Steinar'), new Child(name: 'William')]
}
#ToString(includePackage = false)
#EqualsAndHashCode
class Child {
String name
}
#ToString(includePackage = false)
#EqualsAndHashCode
class Composite {
List<Child> children
}
I have an Entity (Layer) that maps a list of other Entities (Member). This List may have no entries / be null. Yet, when I query for the Entity I get a NOT NULL check constraint error from the database.
It seems to be connected to the NamedQueries as I can read the Entity from DB if I query by id.
#Entity
#NamedQueries({
#NamedQuery(name="getChildLayers",-
query = "SELECT la
FROM Layer la
WHERE la.parent = :parent AND la.deletedDate IS NULL")})
public class Layer extends CommonModel {
/*... other field */
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Layer.class, optional = true)
private Layer parent;
#ManyToMany(fetch = FetchType.LAZY, targetEntity = MyUser.class)
private List<MyUser> members;
public List<MyUser> getMembers() {
return members;
}
public void setMembers(List<MyUser> members) {
this.members = members;
}
/*... other getters and setters */
}
I get this error: integrity constraint violation: NOT NULL check constraint; SYS_CT_10298 table: LAYER_MYUSER column: MEMBERS_ID
I am able to create the entry, though.
When I run my tests then all tests fail that read the Entity (but creation works). If I add the following line in the creation method:
layer.setMembers(new ArrayList<MyUser>());
then the methods that test the alternation of the members work (meaning, I can create a Layer and alter its members by adding and removing elements from the list).
It seems to me that reading the Entity from Database fails whenever there are no Member to the Layer.
I did try adding #JoinColumn(nullable=true) to the field, but it changed nothing.
I import javax.persistence classes.
Example as to how I access the variable (in LayerService)
// this method works as expected
public Layer getById(Long id) {
Session s = sessionFactory.getCurrentSession();
return (Layer)s.get(Layer.class, id);
}
// this does not.
public List<Layer> getChildren(Layer layer) {
Query childrenQuery = sessionFactory.getCurrentSession().getNamedQuery("getChildLayers");
childrenQuery.setParameter("parent", layer);
return (List<Layer>) childrenQuery.list();
}
Code changed after Jason Cs answer:
Layer
...
private final List<OCWUser> members = new ArrayList<>();
...
public void setMembers(List<OCWUser> members) {
this.members.clear();
this.members.addAll(members);
}
Problem still exists.
It can be so simple. I forgot to add #JoinTable
#JoinTable(name = "LAYER_USER", joinColumns = #JoinColumn(nullable = true))
One important thing to be aware of is you shouldn't replace this.members with another list in setMembers unless you know you are doing it before you call persist(). Instead you need to clear this.members then add all the specified elements to it. The reason is that Hibernate can and will use its own proxied / instrumented collections classes when [de]serializing an entity, and you blow that away when overwriting the collection class. You should declare members as final and always initialize it to a non-null empty List.
See for example (3.6 but still relevant): http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/collections.html#collections-persistent, In particular:
Notice how in Example 7.2, “Collection mapping using #OneToMany and
#JoinColumn” the instance variable parts was initialized with an
instance of HashSet. This is the best way to initialize collection
valued properties of newly instantiated (non-persistent) instances.
When you make the instance persistent, by calling persist(), Hibernate
will actually replace the HashSet with an instance of Hibernate's own
implementation of Set.
As long as you are messing with collection fields in this way, any number of strange things can happen.
Also, in general, you want to be careful about stating your invariants and such when accessing collections in this way, as it's easily possible to, e.g., create two Layers that reference the same collection internally, so that actions on one affect the other, or external actions on the passed-in collection affect the layer, e.g. the following code probably doesn't behave like you want it to:
List<MyUser> u = new ArrayList<MyUser>();
Layer a = new Layer();
Layer b = new Layer();
u.add(...);
a.setMembers(u);
b.setMembers(u);
u.clear();
Further, when you persist() one of the layers there, and Hibernate overwrites the field with its own collection class, the behavior then changes as the objects are no longer referencing the same collection:
// not only did u.clear() [possibly undesirably] affect a and b above, but:
session.persist(a);
u.add(...); // ... now it only affects b.
I am working on an object cache of CMS objects. I need to maintain a parent/child relationship between a Product and child objects (Options and Attributes). The parent (Product) is illustrated in the first code sample.
It is easy enough to do, but I am looking for a way to make the assignment of the child to the parent, as shown in the 2nd code block, generic.
Since all CMS objects extend CMSContent, I can use ProductID. However, is there a way to make the field (e.g. ProductAttribute) generic so that I can put the algorithm in a method and call the method with a parent and child object to make the attribute assignment?
I know that an ORM framework like Hibernate is appropriate here, but that won't fit since I have a fixed database structure.
public class Product extends CMSContent {
private List<ProductAttribute> productAttributes;
private List<ProductOptions> productOptions;
// getters,setters
}
Algorithm to match them up.
// attach Product Attributes to Product
for (Product p : listP) {
Map<String, Object> parameters = new HashMap<String, Object>();
for (ProductAttribute po : listPA) {
parameters.put("pid", p.getPid());
parameters.put("ref", po.getRid());
int i = jdbcTemplate.queryForInt(sqlAttr, parameters); // select count(*), 1 if matched.
if (i == 1) {
p.getProductAttributes().add(po); // generic field?
}
}
}
Wouldn't this two Methods in Product help
public void add(ProductAttribute item){
productAttributes.add(item);
}
public void add(ProductOption item){
productOption.add(item);
}
so you should be able to just add a ProductAttribute or a ProductOption
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.