Changing from Collection to SortedSet - java

I'm changing a Collection to a SortedSet because I need it to always be in the same consistent order that they were created in. I've changed my model property from
#OneToMany(cascade = CascadeType.ALL, mappedBy = "contentId")
private Collection<Footnote> footnoteCollection;
to
#OneToMany(cascade = CascadeType.ALL, mappedBy = "contentId")
private SortedSet<Footnote> footnoteSortedSet;
and all relevant functions so Netbeans no longer shows any errors. When I run the app I get the error: Exception Description: Could not load the field named [footnoteSortedSet] on the class [class com.mysite.cmt.model.Content_]. Ensure there is a corresponding field with that name defined on the class.
Since I've just changed this properly and relaunched my app I'm struggling to figure out why it's saying it's not set...

The error you are getting seems to be coming from the JPA metamodel. I assume you are generating this in some way, if you don't use the metamodel in Criteria, then you don't need this and the error will go away.
The issue is that JPA only allows the collection interfaces, Map, List, Set, Collection. So, while you could use a SortedSet in your new instances, object read from the database will use a special lazy List implementation.
In EclipseLink, you can use a SortedSet if you mark the mapping as EAGER.
I think the metamodel error was fixed, try the latest release.

SortedSet javadoc to the rescue:
All elements inserted into a sorted set must implement the Comparable interface (or be accepted by the specified comparator).
Almost certainly, Footnote does not implement Comparable

Related

Hibernate Bidirectional Mapping Results in Cycle when using converter for DTO

I've mapped the classes as follows:
#ManyToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JoinColumn(name = "CATEGORY_ITEMS_ID")
private CategoryItem categoryItem;
#OneToMany(mappedBy="categoryItem",cascade = CascadeType.ALL, orphanRemoval = true)
private List<CategoryRating> categoryRatingList;
But when I need to convert the table models to dto's I'm caught in a cycle as:
target.setCategoryRatingDtoList(categoryRatingConverter.convert(source.getCategoryRatingList()));
target.setCategoryItemDto(categoryItemConverter.convertToDto(source.getCategoryItem()));
both converters end up calling each other.
I need the result as:
List of CategoryItems, in which every CategoryItems object contains a list of associated CategoryRatings
How should I solve this problem? Maybe I'm using bidirectional mapping in the wrong sense. Anyhow, kindly provide your opinion and possible solutions for this problem
You have 3 options.
You could introduce a context and register objects to that context in their constructors. When another object is then in it's constructor it can receive an inflight list/object through that context and thus resolve the cycle this way.
Another option is to make the DTOs mutable and first instantiate the objects as well as register them into a context, before setting the state on the objects. This is similar to the first solution with the slight difference that the DTO class doesn't need to know about the context.
The third solution is that you avoid the cycle by using a simpler converter for e.g. CategoryRating within the CategoryItemConverter that only converts some data, but not the categoryRatings list.

Hibernate Collection vs List as field type

I have two entities, Author and Book, connected with a one-to-many relationship. What's the difference between specifying field type as Collection<Book> and List<Book>? Aforementioned scenario is presented below:
#Entity
public class Author {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "author")
private Collection<Book> books = new ArrayList<>(); // List<Book> instead?
}
The only difference I have already noticed is that when I want to use #OrderColumn annotation I need to use List, but are there any other differences I don't know about? Should I always use Collection if I don't need an order?
Set - contains no duplicates no order
(Bag)Collection - duplicates no order
List - duplicates order
For Set you need to be carefull about hashcode and equals. And one interesting twist with Bags in relation to SQL generated:
If we are using List as a mapped attribute in hibernate without
indexed column, hibernates treats it as a Bag. Since Hibernate handles
List as a Bag (Unordered collection with non unique values. The best
feature of a bag is that you can get the number of occurrences of an
object through the API With a list, there is no way to do the same
without iterating through the whole list.) as soon as we delete and
add a element in this collection. Hibernate issues a SQL to delete all
the elements first from join table which are no supposed to be deleted
and then it re-insert all of them back from the Bag.
http://lkumarjain.blogspot.no/2013/07/why-hibernate-does-delete-all-entries.html
java.util.Collection is the most generic unordered collection of elements while the java.util.List implies existence of an iteration order.
Using #OrderColumn will give this iteration order however it might change the generated SQL query. Often it results in ORDER BY statement added to the SQL query. Without #OrderColumn the JPA provider has more flexibility but you should always measure the performance in your actual database instead of tuning it blindly.

stream on JPA lazy list

I have JPA entity with list like this:
#OneToMany(mappedBy = "scadaElement", orphanRemoval = true)
private List<ElementParameter> elementParameters;
and map form ElementParameter
#ManyToOne
#JoinColumn(name = "SCADAELEMENT_ID")
ScadaElement scadaElement;
when i get entity with elementParameters list and do stream on it stream do nothing, even when I trigger list with .size() but when I do the same with a for loop it work.
System.out.println("elements size: " + s.getElementParameters().size());
s.getElementParameters()
.stream()
.forEach(
a -> {
System.out.println("elementId: " + a.getId());
}
);
Is there any solution to make that stream work? I use eclipselink as JPA provider.
Apparently, you are referring to this issue. These lazy lists using the anti-pattern of inheriting from actual implementations (here Vector) fail to adapt to the evolution of the base class. Note that there are two possible outcomes depending on how the anti-pattern was realized
If the lazily populated list populates itself (it terms of the inherited state) on the first use, the new inherited methods will start working as soon as a trigger property has been accessed for the first time
But if the list overrides all accessor methods to enforce delegation to another implementation, without ever updating the state of the base class, the base class’ methods which have not been overridden will never start working, even if the list has been populated (from the subclass’ point of view)
Apparently, the second case applies to you. Triggering the population of the list does not make the inherited forEach method work. Note that turning off the lazy population via configuration might be the simpler solution here.
To me, the cleanest solution would be if IndirectList inherits from AbstractList and adheres to the Collection API standard, now, almost twenty years after the Collection API has superseded Vector (should I mention how much younger JPA actually is?). Unfortunately, the developers didn’t go that road. Instead, the anti-pattern was maxed out by creating another class that inherits from the class which already inherits from the class not designed for inheritance. This class overrides the methods introduced in Java 8 and perhaps gets another subclass in one of the next Java releases.
So the good news is, developers expecting every List to be a Vector do not have to make up their minds, but the bad news is it doesn’t work as sometimes, you will not get the extended Java 8 specific version with JPA 2.6. But apparently, JPA 2.7 will work.
So you can derive a few alternative solutions:
Turn off lazy population
Stay with Java 7
Wait for JPA 2.7
just copy the collection, e.g.
List<ElementParameter> workList=new ArrayList<>(elementParameters);
This workList will support all Collection & Stream operations
Why not using the real JPA Streaming?
Stream<User> findAllByName(String name);

Grails. Hibernate lazy loading multiple objects

I'm having difficulties with proxied objects in Grails.
Assuming I've got the following
class Order {
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="xxx", joinColumns = {#JoinColumn(name = "xxx")}, inverseJoinColumns = {#JoinColumn(name = "yyy")})
#OrderBy("id")
#Fetch(FetchMode.SUBSELECT)
private List<OrderItem> items;
}
class Customer {
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "xxx",insertable = false, nullable = false)
private OrderItem lastItem;
private Long lastOrderId;
}
And inside some controller class
//this all happens during one hibernate session.
def currentCustomer = Customer.findById(id)
//at this point currentCustomer.lastItem is a javassist proxy
def lastOrder = Order.findById(current.lastOrderId)
//lastOrder.items is a proxy
//Some sample actions to initialise collections
lastOrder.items.each { println "${it.id}"}
After the iteration lastOrder.items still contains a proxy of currentCustomer.lastItem. For example if there are 4 items in the lastOrder.items collection, it looks like this:
object
object
javassist proxy (all fields are null including id field). This is the same object as in currentCustomer.lastItem.
object
Furthermore, this proxy object has all properties set to null and it's not initialized when getters are invoked. I have to manually call GrailsHibernateUtils.unwrapIdProxy() on every single element inside lastOrder.items to ensure that there are no proxies inside (which basically leads to EAGER fetching).
This one proxy object leads to some really weird Exceptions, which are difficult to track on testing phase.
Interesting fact: if I change the ordering of the operations (load the order first and the customer second) every element inside lastOrder.items is initialized.
The question is: Is there a way to tell Hibernate that it should initialize the collections when they are touched, no matter if any elements from the collection is already proxied in the session?
I think what's happening here is an interesting interaction between the first level cache (stored in Hibernate's Session instance) and having different FetchType on related objects.
When you load Customer, it gets put in to the Session cache, along with any objects that are loaded with it. This includes a proxy object for the OrderItem object, because you've got FetchType.LAZY. Hibernate only allows one instance to be associated with any particular ID, so any further operations that would be acting on the OrderItem with that ID would always be using that proxy. If you asked the same Session to get that particular OrderItem in another way, as you are by loading an Order containing it, that Order would have the proxy, because of Session-level identity rules.
That's why it 'works' when you reverse the order. Load the Order first, it's collection is FetchType.EAGER, and so it (and the first level cache) have fully realized instances of OrderItem. Now load a Customer which has it's lastItem set to one of the already-loaded OrderItem instances and presto, you have a real OrderItem, not a proxy.
You can see the identity rules documented in the Hibernate manual:
For objects attached to a particular Session... JVM identity for database identity is guaranteed by Hibernate.
All that said, even if you get an OrderItem proxy, it should work fine as long as the associated Session is still active. I wouldn't necessarily expect the proxy ID field to show up as populated in the debugger or similar, simply because the proxy handles things in a 'special' way (ie, it's not a POJO). But it should respond to method calls the same way it's base class would. So if you have an OrderItem.getId() method, it should certainly return the ID when called, and similarly on any other method. Because it's lazily initialized though, some of those calls may require a database query.
It's possible that the only real problem here is simply that it's confusing to have it so that any particular OrderItem could be a proxy or not. Maybe you want to simply change the relationships so that they're either both lazy, or both eager?
For what it's worth, it's a bit odd that you've got the ManyToMany relationship as EAGER and the ManyToOne as LAZY. That's exactly the reverse of the usual settings, so I would at least think about changing it (although I obviously don't know your entire use case). One way to think about it: If an OrderItem is so expensive to fetch completely that it's a problem when querying for Customer, surely it's also too expensive to load all of them at once? Or conversely, if it's cheap enough to load all of them, surely it's cheap enough to just grab it when you get a Customer?
I think you can force eager loading this way or using
def lastOrder = Order.withCriteria(uniqueResult: true) {
eq('id', current.lastOrderId)
items{}
}
or using HQL query with 'fetch all'

SortedMap Key Persistance in Hibernate

I'm hoping someone can help me with my hibernate issue as I've been banging my head against Google for around an hour without result.
So the issue is that I have a SortedMap in a class, using Integer as the Key (and its natural built-in compareTo method) and another class as the value type. I'm using the key to keep the user-defined order of the value type and trying to get Hibernate to persist this.
For whatever reason, Hibernate has defaulted to disregarding the key I have inputted and instead using the value type's primary key as the key instead. When I load my Map back out of the database all of my keys have been changed in this way.
Definition of the Map is shown below (I'm using annotation-style Hibernate);
#ManyToMany(cascade = CascadeType.ALL)
#MapKey
#Sort(type = SortType.NATURAL)
private SortedMap<Integer, Column> columnOrder;
I can't use the Column type to store the order itself as the Column may be used in many instances of the containing type, with a different key value each time. Any guidance would be most appreciated.
So I found the answer after discovering this StackOverflow question with a similar issue: Sorted map of Java primitives using JPA2 and Hibernate?
By changing the #MapKey annotation to the #MapKeyColumn annotation, I was able to give Hibernate the instruction to persist the key in the column name I specified, as below;
#ManyToMany(cascade = CascadeType.ALL)
#MapKeyColumn(name = "hierarchyOrdering")
#Sort(type = SortType.NATURAL)
private SortedMap<Integer, Column> columnOrder;
Thanks for the help.
From the javadoc of javax.persistence.OrderColumn:
Specifies a column that is used to maintain the persistent order of a list. The persistence provider is responsible for maintaining the order upon retrieval and in the database. The persistence provider is responsible for updating the ordering upon flushing to the database to reflect any insertion, deletion, or reordering affecting the list.
So it is possible to use a list for that.
The JPA 2.0 spec states in section 2.2 Persistent Fields and Properties:
Collection-valued persistent fields and properties must be defined in terms of one of the following collection-
valued interfaces regardless of whether the entity class otherwise adheres to the JavaBeans
method conventions noted above and whether field or property access is used: java.util.Collection,
java.util.Set, java.util.List[3], java.util.Map. The collection implementation
type may be used by the application to initialize fields or properties before the entity is made
persistent. Once the entity becomes managed (or detached), subsequent access must be through the
interface type.
So it seems as if a SortedMap is not supported by JPA.

Categories

Resources