How to create a HashSet from a Set? - java

There is a foreign key in my entity :
#Entity
#Table(name = "role")
public class Role {
#Id
#Column(name = "role_code")
private String code;
#Column(name = "role_lib")
private String lib;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "role_menu" , joinColumns = {#JoinColumn(name = "role_code")} , inverseJoinColumns = {#JoinColumn(name = "menu_id")} )
#JsonManagedReference
private Set<Menu> menus = new HashSet<Menu>();
// getters and setters
}
It is said in Hibernate documentation that relationship attributes should be of type Interface. Now my problem is when dealing with an instance of this class and using the getMenus() method :
#Override
#Transactional
public Set<Menu> getListMenu(String role_code) {
return ((Role)get(role_code)).getMenus();
}
I want to cast it to a HashSet , but I got castException of persistent object at runtime. So how to make it to be HashSet ?

You cannot cast if the implementations of Set is not HashSet. But you can create a object:
new HashSet(obj.getMenus);
But it's always better to use interfaces, not the implementation.
Here is a note from Hibernate doc:
Hibernate will actually replace the HashSet with an instance of
Hibernate's own implementation of Set. Be aware of the following
errors:
......
(HashSet) cat.getKittens(); // Error!
And here is why you don't actually need to cast nor to create a new object:
The persistent collections injected by Hibernate behave like HashMap,
HashSet, TreeMap, TreeSet or ArrayList, depending on the interface
type.

Related

Duplicating elements in a ManyToMany relation

I have a ManyToMany relationship between Profile and ProfileExperience that is mapped as follows:
#ManyToMany
#JoinTable(name = "profile_experience_relations",
joinColumns = {
#JoinColumn(name = "profile_id")
},
inverseJoinColumns = {
#JoinColumn(name = "profile_experience_id")
})
private List<ProfileExperience> experiences;
I have added localization support inside of ProfileExperience, following this guide like so:
ProfileExperience Class
#OneToMany(mappedBy = "profileExperience", cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, orphanRemoval = true)
#MapKey(name = "localizedProfileExperiencePk.locale")
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private Map<String, LocalizedProfileExperience> localizations = new HashMap<>();
LocalizedProfileExperience Class
#Entity
#Getter
#Setter
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class LocalizedProfileExperience {
#EmbeddedId
private LocalizedProfileExperiencePk localizedProfileExperiencePk;
#ManyToOne
#MapsId("id")
#JoinColumn(name = "profileExperienceId")
private ProfileExperience profileExperience;
private String value;
}
Composite PK Class
#Embeddable
#Getter
#Setter
public class LocalizedProfileExperiencePk implements Serializable {
private static final long serialVersionUID = 1L;
private String profileExperienceId;
private String locale;
public LocalizedProfileExperiencePk() {
}
Before adding the localization, there was no duplicate entries in the responses, however - everything retrieved is now duplicated.
I can solve the issue by using a Set, however I'm curious as to why this happened. What is the explanation? Can I solve it without using a set? Am I overlooking something incredibly simple?
The problem is that you are probably using join fetch or an entity graph to fetch nested collections. Now, when you look at the JDBC result set, you will see that there are many duplicate result set rows. If you have a profile with 2 profile experiences, and each has 3 localizations, you will see that you have 6 (2 * 3) duplicate rows. Theoretically, Hibernate could try to retain the expected object graph cardinality, but this is not so easy, especially when multiple collections are involved. Also, for certain collection mappings it would simply not be possible to do.
So the short answer to your problem is, never use a List unless duplicity matters to you. In this case, you will have an order column though, so even then it would be safe to use a list.
Implement the equal method of your data class. Hibernate need it.

Why children entities are duplicated in DB everytime i save parent object

I have entities "ZakladProdukcyjny" and "MiejsceProwadzeniaDzialnosci".
There is an unidirectional relation #OneToMany with a join table.
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "ZAKLAD_PRODUKCYJNY_MIEJSCE_PROWADZENIA_DZIALALNOSCI",
joinColumns = {
#JoinColumn(name = "zakladProdukcyjny_ID")},
inverseJoinColumns = {
#JoinColumn(name = "miejsceProwadzeniaDzialalnosci_ID")})
private List<MiejsceProwadzeniaDzialalnosci> miejscaProwadzeniaDzialalnosci = new ArrayList<>();
I am using Spring JPARepositories
public interface ZakladProdukcyjnyRepository extends JpaRepository<ZakladProdukcyjny, Long>,
Everytime i am saving the parent entity with zakladProdukcyjnyRepository.save(zakladProdukcyjny), children entities are being persised into DB so everytime save is executed on the JPARepository i am having duplicated entries.
The child entity uses a lombok for generating equals and hashcode.
#EqualsAndHashCode(callSuper=false)
public class MiejsceProwadzeniaDzialalnosci extends BaseEntity {
I have no idea what may be wrong here.
This should have beed fixed long time ago:
https://hibernate.atlassian.net/browse/HHH-5855
https://hibernate.atlassian.net/browse/HHH-6776
Try changing the List to a Set or remove CascadeType.ALL and leave just CascadeType.MERGE.
I have solved the problem. The issue was an equals functionality. Somewhere in the code i had:
for (MiejsceProwadzeniaDzialalnosci mpd : uaktualnioneMiejscaProwadzeniaDzialalnosciZBDO) {
if (!(zaklad.getMiejscaProwadzeniaDzialalnosci().contains(mpd))) {
zaklad.getMiejscaProwadzeniaDzialalnosci().add(mpd);
}
}
after ovveriding the equals method there is no duplicates.

Hibernate map an association as a map

I have a MessengerData class which contains a list of resources. This my object MessengerData:
"messengerData":{
"fr":[
{
"messengerType":"ImageCategoryTitle",
"imageURL":"https://assets.pernod-ricard.com/uk/media_images/test.jpg"
}
"EN":[
{
"messengerType":"ImageCategoryTitle",
"imageURL":"https://assets.pernod-ricard.com/uk/media_images/test.jpg",
}
]
This is how I define my object MessengerData:
#Entity
public class MessengerData
{
#Basic
#Id
#GeneratedValue(generator = "notification-system-uuid")
#GenericGenerator(name = "notification-system-uuid", strategy = "uuid")
private String messengerDataId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) /* , mappedBy = "idResource" */
#JoinTable(name = HemisTablesNames.MESSENGER_RESOURCES, joinColumns = #JoinColumn(name = "idResource"),
inverseJoinColumns = #JoinColumn(name = "messengerDataId"))
private Map<String, Set<Resource>> resources;
}
But I am getting this exception: Use of #OneToMany or #ManyToMany targeting an unmapped class: com.ubiant.hemis.type.MessengerData.resources[java.util.Set]
Could someone help me with this ?
Hibernate doesn't seem to support multimaps (that's what resources is) directly but you could provide your own custom type like described here: https://xebia.com/blog/mapping-multimaps-with-hibernate/ .
However, since your data seems to be Json anyway you could go one more step and directly map the resources as json, i.e. into a text column (or a json column if the db supports it): http://fabriziofortino.github.io/articles/hibernate-json-usertype/
We're doing something similar, which on an outline looks like this (this is a generic type, in most cases a more specific POJO will be better):
class JsonData extends HashMap<String, Object> { ... }
//JsonbUserType is a custom implementation based on code like the one linked above
class JsonDataUT extends JsonbUserType<JsonData > { ... }
Then in package-info.java of the package the user type is in we have this:
#TypeDefs ( {
#TypeDef ( name = "JsonDataUT ", typeClass = JsonDataUT.class, defaultForType = JsonData.class ),
...
})
package our.package;
And our entities then just contain this:
#Column( name = "data_column")
private JsonData data;
One advantage of this is that we don't have to bother with more complex mappings, especially if types are dynamic.
One (major) disadvantage, however, is that you can't use that property in query conditions since Hibernate wouldn't know how to filter in a json column (we're using Postgres so it would really be a jsonb typed column, hence the usertype name) and afaik there's not reasonable way to provide custom functions etc. to enable things like where data.someFlag is true in HQL.

Persisting EnumSet in Postgresql with Hibernate

Effective Java, Item 32, states Use EnumSet instead of bit fields. I also found this nice tutorial on the topic. This book has been around for a while, so why don't I find any posts on how to persist an EnumSet with Hibernate? Well, I actually found this one, and another one, but they are both quite old, point to the same and much older solution, which unfortunately did not help me, perhaps because of my lack of deeper hibernate knowledge? Here is an abstract of my code:
public class MyThing {
public enum MyOptions {
FLAG1, FLAG2
}
#Id
private Long id;
#Column(name = "options")
private EnumSet<MyOptions> options;
// [other fields, getters, setters etc]
}
I've tried other annotations like
#ElementCollection
with and without (targetClass = MyOptions.class)
and
#JoinTable(name = "my_options",
joinColumns = #JoinColumn(name = "id"))
and also
#OneToMany
#JoinColumn(name = "options")
but with no luck.
Preferably, I'd store the information in a new column of the my_thing table, but I could also live with a separate table for the enums, if required.
Try this
#ElementCollection
#CollectionTable(name = "my_options",
joinColumns = #JoinColumn( name = "mything_id"))
#Column(name = "option")
#Enumerated(EnumType.STRING)
private Set<MyOptions> options;
With this configuration, you need a database table named my_options with columns option and mything_id which targets MyThing table.

Mapping a map of two abstract entities in Hibernate

I am trying to Map the following data structure in hibernate:
#OneToMany(fetch = FetchType.EAGER,cascade=Array(CascadeType.REMOVE,CascadeType.REFRESH,CascadeType.MERGE,CascadeType.PERSIST))
#JoinTable(name = "links", joinColumns = Array(new JoinColumn(name = "link_id")))
#MapKey(name="id")
#Fetch(value = FetchMode.SELECT)
#Access(AccessType.PROPERTY)
def getLinksMapNative:java.util.Map[MyClass,MyClass] = {
linksMap
}
I have the following problem though:
If I leave the MapKey annotation, when loading from the session, the Key of the Map is the integer id of MyClass instead of being an instance of MyClass and this result into a ClassCastException at runtime
If I take it off, since MyClass is an abstract entity I get:
org.hibernate.InstantiationException: Cannot instantiate abstract class or interface
at the moment of persisting.
Can one map a Map of two abstract entities in Hibernate, and if yes, how?

Categories

Resources