JPA #ManyToMany join table indexing - sql

Hibernate allows adding indexes on #ManyToOne mappings by the use of #org.hibernate.annotations.Index.
Is there a way to specify index for the join table in a #ManyToMany relation?
If Entity A and Entity B have a #ManyToMany with A being the owning side, the join table will have a composite index (a1, b1).
I wonder if this is enough, or do I need to create another index (b1, a1)?

Answering a 6 year old question here, still relevant though. I came across this question while facing the same issue. Searching the web makes it look like JPA does not support indexes on join tables but it does, this is documented here.
When using #JoinTable you can specify the indexes on the annotation, for example
#ManyToMany()
#JoinTable(name = "car_driver",
joinColumns = #JoinColumn(name = "car_id"),
inverseJoinColumns = #JoinColumn(name = "driver_id"),
indexes = {
#Index(name = "idx_car_driver_car_id", columnList = "car_id"),
#Index(name = "idx_car_driver_driver_id", columnList = "driver_id")
}
)
private Set<Driver> drivers;

I too am searching for this - the holy grail, it seems.
In this post:
How can I ask Hibernate to create an index on a foreign key (JoinColumn)? an answerer seems to believe that adding #Index to the collection creates an index on the join table. It doesn't.
This post:
How do I create an index on join tables using Hibernate annotations? reveals what I am slowly coming to believe is the horrible truth. It's not possible, unless you revert to using an hbm.xml file to define your entities (ie: doesn't appear to be possible using annotations).
So whilst it does speed up queries tremendously to have a composite index on join tables, encompassing both FK columns, but there is currently no way to create said index automatically using annotations.
I am now looking into creating a task to be used after Hibernate's ExportSchema execution to identify join tables and create indexes. It's not going very well! Please do share any viable solutions you happen to come across. I'm hoping someone will share their approach for comparison.

I thought about it and I find solution. In case of many to many relation I used
#ManyToMany
#JoinTable(name = "core_auth_role",
joinColumns = { #JoinColumn(name = "auth_id") },
inverseJoinColumns = { #JoinColumn(name = "role_id") },
uniqueConstraints = { #UniqueConstraint(columnNames = {"role_id", "auth_id" }) })
#ForeignKey(name = "fk_testkey", inverseName = "fk_testkey_inverse")
private Set<Role> roles;
In result I have two idexes (auth_id, role_id) and (role_id, auth_id).
Schema created by Hibernate:
create table SOME_TABLE (
...
primary key (auth_id, role_id),
unique (role_id, auth_id)
);
primary key and unique cosntrainst are automatically indexed in database.

I came across this https://www.baeldung.com/jpa-many-to-many while looking for a way how to have control over the join table, so that I could have at least one of its keys indexed.
It breaks down the many-to-many relation into a many-to-one relation on the join table side and a typical OneToMany on each of the two associated entities, associating them to the join table, like so:
#Entity
class Student {
#OneToMany(mappedBy = "student")
Set<CourseRating> ratings;
}
#Entity
class Course {
#OneToMany(mappedBy = "course")
Set<CourseRating> ratings;
}
#Entity
class CourseRating {
#ManyToOne
#MapsId("student_id")
#JoinColumn(name = "student_id")
Student student;
#ManyToOne
#MapsId("course_id")
#JoinColumn(name = "course_id")
Course course;
}
The #Index annotation can be used on any of the fields in the CourseRating (join table) entity. In my case I used a composite key on the join entity because of reads on its table, so I actually needed to split the relation.
I hope this can be of help.

Related

Course has many students and many students belong to one course. How to find to which course a particular student belong?

there is a one to many unidirectional mapping from course to students
#Entity
Course {
#OnetoMany
List<Student> students;
}
#Entity
Student {
name;
age;
}
the database schema looks like
Course
id name duration fee
Student
rollno name age course_id
I am using Jpa repository for getting.
Can anyone please tell how to tell in which course a particular student belong to? Please note that i am not using bi-directioal mapping so student entity class has no reference to course.
Your mappings and DB schema are fine, and there is nothing overly special about your mapping situation - it is just a unidirectional mapping, and there is no need for force mapping it bidirectionally and/or fetching the student data if you don't need it.
JPQL: "Select c from Course c join c.students s where s.name = :name"
With spring boot
#Query("Select c from Course c join c.students s where s.name = ?1")
Course findCourseByStudentName(String name);
Your DB setup would need to be looked at to determine if the Course is unique - I'd guess it isn't, in which case you'll want to return a list of courses that might have this student name registered.
Your problem is in the database deign. No performant code will rescue you from that fate.
Typically there is a join table between Students and Courses that minimally maintains two keys - the studentId and the courseId.
Need to know the courses for a student? Select from the join table where studentId = ‘X’. Need to know students in a course? Select from the join table where courseId = ‘Y’.
I won't comment on the DB design. But solution for this problem is simple.
Basically you need to implement the relationship correctly.
#Entity
public class Course {
private Long id;
#OnetoMany(mappedBy = "course")
List<Student> students;
}
#Entity
public class Student {
private Long id;
private String name;
private Integer age;
#ManyToOne
#JoinColumn(name="course_id")
private Course course;
}
Then simply use the getter
Student student = studentRepo.findStudentById(123);
Course course = student.getCourse();
Here is the full reference: https://www.baeldung.com/hibernate-one-to-many

Eclipselink Shared Cache - Lazy associations

I'm devoloping an application using JPA eclipselink 2.6.4.
I'm enabling the shared cache for serveral JPA entities.
#Entity
#Cache(
type=CacheType.SOFT,
size=500
)
public class Tutor
{
#Id
#Column(name = "id")
private String id;
#Column(name = "name")
private String name;
... getters and setters
}
#Entity
#Cache(
type=CacheType.SOFT,
size=500
)
public class Student
{
#Id
#Column(name = "id")
private String id;
#Column(name = "name")
private String name;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="TUTOR_ID")
private Tutor tutor;
... getters and setters
}
I got a typical entity association of tutor and students. It is defined as LAZY because we want to avoid the "N + 1 problem" when a students query is done.
When we execute a students query and we want the association to be resolved, we use JOIN FETCH technique:
SELECT s FROM Student s JOIN FETCH s.tutor t WHERE s.id = :id
Up here all right.
The first time we execute the JPA query, because shared cache has no students yet, a database query is executed. We get the student entity populated with the tutor entity.
Nevertheless, the second time we execute the same query, there is a hit in the shared cache, so a database query is NOT executed. But then the student entity IS NOT populated with the tutor entity.
We would like to get the student entity populated with the tutor entity.
We have seen that this is possible changing the association to EAGER, but we don't want to change to EAGER to avoid "N + 1 problem".
Any idea?
Thanks

Django ORM - understanding foreign key queries

I'm in a process of optimizing my queries. Assume I have these models:
class Author(models.Model):
name = models.CharField(max_length=20)
class Book(models.Model):
name = models.CharField(max_length=20)
author = models.ForeignKey(Author)
A simple task here would be to get all the books of a given author, assume I have the author-ID.
In standard SQL I would only need to query the books table.
But In django code I do:
# given authorID
author = Author.objects.get(pk=authorID)
books = Book.objects.filter(author=author)
Which would take two queries. How can I avoid the first query ?
Try something like:
Book.objects.filter(author_id=authorID)
This will return all the books where author's foreign key is authorID.

hibernate criteria: select object from one table using another one

First of all sorry if my question is't very well formulated.
I have simple object which is mapped to corresponding table:
#Entity
#Table(name = "USERS")
public class User{
#Id
#Column(name = "USER_ID")
#GeneratedValue
private long userId;
#Column(name = "NAME")
private String name;
//getters and setters...
In my database I also have another table. There isn't any associated object with this table but it has column "USER_ID" - the same id as in the "USERS" table.
So the question is, how to select all USERs from the first table which have the same USER_ID as in the second table using Hibernate criteria. For example I have two Users with userId =1 and =2. In second table in column "USER_ID" I have only value "2". So I need only User with userId =2 from the first table.
Your query is classic example of FOREIGN_KEY relation but as u said u don't have Hibernating mapping Model for another table.It's better to use sql-query in your user mapping file
Collect this query result in User object.
Something like this
<sql-query name="userwith">
<return alias="user" class="User"/>
SELECT
user.Name AS {user.name}
FROM USERS
JOIN ANOTHER_TABLE another_table
ON user.ID = another_table.USER_ID
</sql-query>

How to build custom PLINQO query by multiple id's?

Here's my table structure
Places
PlaceId PK
Name
...
PlaceCategories
CatId PK
Name
...
PlaceCats
PlaceId PK
CatId PK
Here's my query that pulls Places based on category id (table join)
public static IQueryable<Places> ByPlaceCat(this Table<Places> table, Expression<Func<PlaceCats, bool>> predicate) {
var db = (DataContext)table.Context;
var innerBizBase = db.PlaceCats.Where(predicate);
return db.Places.Join(innerBizBase, a => a.PlaceId, ab => ab.PlaceId, (a, ab) => a);
}
I use it like this:
places = Db.Places.ByPlaceCat(a => a.CatId == 5);
But I want to be able to pull based on a List<int> of category id's. Looking through the generated PLINQO code, a query that pulls by multiple PlaceId's (but not using a joined table) looks like this:
public static IQueryable<Places> ByPlaceId(this IQueryable<Places> queryable, IEnumerable<long> values)
{
return queryable.Where(p => values.Contains(p.PlaceId));
}
How could I essentially merge those two queries, to let me pass in a List<int> of CatId's to query by? This LINQ/PLINQO query is melting my brain. Thanks in advance!
You would need to write a extension method like this:
public static IQueryable<Places> ByPlaceCats(this Table<Places> table, IEnumerable<int> catIds)
{
var db = (TestDataContext)table.Context;
var places = (from placeCat in db.PlaceCats
join place in db.Places on placeCat.PlaceId equals place.PlaceId
where catIds.Contains(placeCat.CatId)
select place);
return places;
}
Please note that the PlaceCats table could be made into a ManyToMany relationship by adding two foreign keys to the proper tables. Once this change has been made than PLINQO will automatically generate the correct code and will create a link between the two tables skipping the intermediary table. So you could get a collection of PlaceCategories associated to the current Places entity by accessing a property on the Places entity.
Please remember to contact us if you have any questions and be sure to check out the community forums located here and PLINQO forums here.
Thanks
-Blake Niemyjski (CodeSmith Support)