What is HIBERNATE_IDX with #JoinTable - java

I never do this, but someone on my project created a many to many relationship between, let's say, a Foo and a Bar. Foo and Bar both have unique system generated IDs. On Foo, I have the following code:
#ManyToMany(targetEntity = Bar.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "FOO_BAR_LNK",
joinColumns = {#JoinColumn(name = "FOO_ID", referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name = "BAR_ID", referencedColumnName = "ID")})
private Set<Bar> bars;
When the table gets created, it has 3 columns, HIBERNATE_IDX, FOO_ID and BAR_ID. HIBERNATE_IDX contains all zeroes.
What is HIBERNATE_IDX?

It appears that HIBERNATE_IDX is part of a tie-breaker to guarantee unique indices on join tables and prevent cartesian products. Kind of pieced together, but that's what I think.

Related

Java springboot - Best way to structure my Object relationships for performance?

I am using SQL & Java Springboot.
In my design, content is stored in modules & content have quite a few different variations.
Content can only be designated to a single module, once content is created it cannot updated to another module because different admin users have separated access to different module moderation.
The way activities are stored is with a foreign key table between, eg. extra & module.
#OneToMany
#JoinTable(name="module_extras",
joinColumns = #JoinColumn(name="module_id"),
inverseJoinColumns = #JoinColumn(name = "extra_id")
)
private List<Extra> extras = new ArrayList<>();
Because it is stored this way, every time I want to get only extra or a list of only extra, (which is a quite common use case as I have a designated area for it) it must:
Recursively loop to get each extra requested, temporarily convert it to a DTO.
Recursively loop over each module, checking if the extra is contained inside it.
If it find the corresponding extra to module, appending the module.code to the extra-moduledto.
then reply with all the extra-moduledto requested.
Given it must loop this logic, for every module and for all content's inside module (when called for content sections independently), potentially many, many times to support users refreshing & interacting with content, this seems like it would be bad on performance & has a multiple points of potential failure / errors.
The alternate solution I am considering is adding a moduleCode column to each content, like extra and demolish the relationship.
1.While it would take more storage doing this ^, would performance be better from doing far less number of operations?
Is the above solution against convention?
If anybody has any suggestions or thoughts that may be useful from your own experience it would be greatly appreciated.
Module Class:
#Data #NoArgsConstructor #AllArgsConstructor #Entity
#Table(name = "module")
public class Module {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique=true)
private String code;
#Nullable
private String name;
#ManyToMany
#JoinTable(name="module_students",
joinColumns = #JoinColumn(name="module_id"),
inverseJoinColumns = #JoinColumn(name = "user_id")
)
private Set<User> students = new HashSet<>();
#ManyToMany
#JoinTable(name="module_admins",
joinColumns = #JoinColumn(name="module_id"),
inverseJoinColumns = #JoinColumn(name = "user_id")
)
private Set<User> admins = new HashSet<>();
#OneToMany
#JoinTable(name="module_quizzes",
joinColumns = #JoinColumn(name="module_id"),
inverseJoinColumns = #JoinColumn(name = "quiz_id")
)
private List<Quiz> quizzes = new ArrayList<>();
#OneToMany
#JoinTable(name="module_hangmen",
joinColumns = #JoinColumn(name="module_id"),
inverseJoinColumns = #JoinColumn(name = "hangnman_id")
)
private List<Hangman> hangmen = new ArrayList<>();
#OneToMany
#JoinTable(name="module_extras",
joinColumns = #JoinColumn(name="module_id"),
inverseJoinColumns = #JoinColumn(name = "extra_id")
)
private List<Extra> extras = new ArrayList<>();
#OneToMany
#JoinTable(name="module_matches",
joinColumns = #JoinColumn(name="module_id"),
inverseJoinColumns = #JoinColumn(name = "match_id")
)
private List<Match> matches = new ArrayList<>();
#OneToMany
#JoinTable(name="module_feedbacks",
joinColumns = #JoinColumn(name="module_id"),
inverseJoinColumns = #JoinColumn(name = "feedback_id")
)
private List<Feedback> feedbacks = new ArrayList<>();
#OneToMany
#JoinTable(name="module_swipes",
joinColumns = #JoinColumn(name="module_id"),
inverseJoinColumns = #JoinColumn(name = "swipe_id")
)
private List<Swipe> swipes = new ArrayList<>();

Many To Many order by time it got added to post

#Entity
public class Post{
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "tag_posts", joinColumns = #JoinColumn(name = "post_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "post_id", referencedColumnName = "id"))
private Set<Tag> hashtags= new LinkedHashSet<>();
}
Hello I would like to sort hashtags by right order they got added in to post, is anything like this possible?
Or would I just change type to List? Wouldnt this affect performance?
Thanks.
I assume you want to retrieve Post along with the tags but you want tags in the order it got added to the Post.
You can use #OrderBy annotation to get the list of tags while retrieving.
#Entity
public class Post{
private Long id;
#OrderBy(value="orderField")
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "tag_posts", joinColumns = #JoinColumn(name = "post_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "post_id", referencedColumnName = "id"))
private Set<Tag> hashtags= new LinkedHashSet<>();
}
If you want to create a column in the join table which will keep the same order you can use #OrderByColumn("orderField")

Many to Many Not Respecting Update and Insert

I am trying to setup and Many to Many relationship between users and roles. I am mostly using JPA Repositories but I also tried using and EntityManger.
I have the following in my main User object.
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "svcAuthUserRolev2", schema="dbo", joinColumns = {
#JoinColumn(name = "user_id", updatable=false,insertable=false, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "role_id",
updatable=false,insertable=false, nullable = false) })
private Set<AuthRoleEntity> roles;
And the following in my Roles object
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
private Set<AuthUserEntity> users;
No matter what I do if I make changes to the roles on a user when saving they are persisted and this is not what I would like. I want roles on the user object to be read only.
I'm not sure why updateable and insertable are not working, I haven't used those attributes much. One possible solution is to make AuthRoleEntity the owning entity of the many-to-many relationship. Just move the #JoinTable annotation to the AuthRoleEntity and put the mappedBy on the AuthUserEntity.

Why is #ManyToMany not working with non-primary key columns?

I have 2 entities - User and Role which have following relations: User has a manytomany relation to itself and a manytomany relation with the Role entity.
#Entity
public class UserEntity implements Serializable {
#Id
#Column(length = 12, columnDefinition = "BINARY(12)", name = "Id", unique = true)
private byte[] id;
#Column(name = "Login", unique = true, nullable = false)
private String login;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "User_Role",
joinColumns = { #JoinColumn(name = "UserLogin", referencedColumnName = "Login") },
inverseJoinColumns = { #JoinColumn(name = "RoleId", referencedColumnName = "Id") })
private Set<RoleEntity> roles;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "User_User",
joinColumns = { #JoinColumn(name = "UserParent") },
inverseJoinColumns = { #JoinColumn(name = "UserChild") })
private Collection<UserEntity> children;
...
}
and Role:
public class RoleEntity implements Serializable{
#Id
#Column(name = "Id", unique = true, nullable = false)
private String id;
...
}
The strange thing about the setup of DB is that the User_User relation is based on the binary Id keys
create table if not exists User_User (
UserParent binary,
UserChild binary
);
and the user-role is based on varchars
create table if not exists KNUser_UserRole (
UserLogin varchar,
RoleId varchar,
);
Now, when it runs, the user-user relationship work well. However, when I try to access the collection returned for roles, I get a ClassCastException:
java.lang.ClassCastException: **.entity.UserEntity cannot be cast to [B
at org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor.extractHashCode(PrimitiveByteArrayTypeDescriptor.java:41)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:201)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:205)
at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:114)
at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:79)
at org.hibernate.internal.AbstractSessionImpl.generateEntityKey(AbstractSessionImpl.java:240)
at org.hibernate.engine.internal.StatefulPersistenceContext.getCollectionOwner(StatefulPersistenceContext.java:740)
at org.hibernate.loader.Loader.readCollectionElement(Loader.java:1181)
at org.hibernate.loader.Loader.readCollectionElements(Loader.java:800)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:651)
at org.hibernate.loader.Loader.doQuery(Loader.java:856)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:289)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2175)
at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:61)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:622)
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:82)
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1606)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:379)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:112)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)
It looks like the UserEntity is being cast to some binary(?) thing. However, the first relation between users themselves works fine, but the one with another table is wrong.
I am using different columns of different types to join tables. Is it allowed to do it this way?
Another strange thing is that when I switch the #Id annotation to be on the login field, the roles work fine, no issue, but then of course the self-join PersistentBag key is the Login instead of Id, which breaks the relation and no results are retrieved. But the conversion from UserEntity to the "[B" is not done.
Also if I leave things as in example and change the Id type to String (and the DB to varchar) it also starts working (of course not consistently with the User_User table).
What am I doing wrong? What is the reason for getting the classcastexception in this case? Why it work when I change the byte[] to String? Please let me know if you have any ideas. I do not want to change the DB design cause this would lead to lots migration and compatibility issues for clients already using the DB.
Just a note: the #Id has to be on the Id binary field as otherwise I wouldn't be able to make a self-join (I was unable to point twice to a column not being a primary key see: Is Hibernate ManyToMany self-join possible for non-key columns? getting mappingException).
Cheers
Adam
the referred column in your join table must be unique entry, here if you put #Id on login field then it works fine,but when you change it to different other than #Id column you cant be sure about the entries will be unique.what you can do is,
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "User_Role",
joinColumns = { #JoinColumn(name = "UserLogin", referencedColumnName = "Id") },
inverseJoinColumns = { #JoinColumn(name = "RoleId", referencedColumnName = "Id") })
private Set<RoleEntity> roles;
I think it should work.

Ordered hibernate relationships

I have two entities, Books and Wishlist. My wish list contains books. I want to get the collection of books that are in my wish list ordered in the same order they were added to the wish list.
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "WISHLISTS_BOOKS", joinColumns = { #JoinColumn(name = "BOOK_ID") }, inverseJoinColumns = { #JoinColumn(name = "WISHLIST_ID") })
private Set<Book> books;
This is where I get stuck. There's an #OrderBy annotation but from what I understand it refers to a field in the book to use to order by. In my case, since a book can belong to multiple wish lists, I can't do that. Instead, I tried to create a added_ts timestamp field in the WISHLISTS_BOOKS join table but I'm not sure how to refer to it.
Can anyone help me sort this out?
You can use #OrderColumn(name = "added_ts") to achieve ordering.
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "WISHLISTS_BOOKS", joinColumns = { #JoinColumn(name = "BOOK_ID") }, inverseJoinColumns = { #JoinColumn(name = "WISHLIST_ID") })
#OrderColumn(name = "added_ts")
private Set<Book> books;

Categories

Resources