How to fetch the association table columns using Spring Data JPA? - spring

I have Three tables 1) student 2) game and 3)student_game. Here studentid is the primary key of student table and gameid is the primary key of the game table. The association table has four columns 1) uuid 2) studentid 3) gameid 4) gametype.
I am using the spring data jpa, So my student entity class has relation as follows,
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name = "student_game", joinColumns = #JoinColumn(name = "studentid"), inverseJoinColumns = #JoinColumn(name = "gameid"))
private Set<Game> GameSet;
Game entity has the following relation,
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name = "student_game", joinColumns = #JoinColumn(name = "gameid"), inverseJoinColumns = #JoinColumn(name = "studentid"))
private Set<Student> studentSet;
In my business layer, I am able to fetch the set of game names for respective student by using the following logic,
Student s = studentRepository.findOne(1L);
Set<Game> games= dd.getGamesSet();
games.forEach(game-> System.out.println(game.getGameId() + " : " + game.getGameName()));
Now My question is,
I need to fetch the gameType column value from the association table (student_game) by passing input as student id.
How can I fetch the association column values?
Please suggest the best approach to fetch the association table column values.

Your mapping is wrong:
you're mapping the same, bidirectional, ManyToMany association as two different OneToMany associations, but using the same join table. This can cause undefined behavior, since you might, in the same transaction remove a game from a player, and add the player to the game.
You can't use the student_game table as a join table: every time a game is added to a player (or vice-versa), since Hibernate doesn't know anything about the uuid and gametype columns, it won't insert anything into these two columns, and this is not what you want.
What you need todo is map the student_game table as another entity. Let's call it a Play. Then, a Play has a gameType, field, one User has multiple Plays; one Game has multiple Plays. And you may of course make these two OneToMany associations bidirectional.

Related

spring data jpa + unwanted unique key generation

I am using spring data jpa for creation of my tables. My Requirement.
I have two Tables.
Basket Table - It has one to many Relationship with the Item table. A basket can have many Items.
Item Table - Here an Item can be associated with many Baskets.
I am using IGNORE_ROW_ON_DUPKEY_INDEX to make sure that the same combination of basketId and itemId, does not get persisted
So there is a mapping table in between in which holds the mapping of baskets and items. Now, in this table, i want the combination of basketId and itemId to be unique. Below is my entity stucture.
#Entity
Class Basket{
#Id
private long basketId;
...
#OneToMany(cascade = {CascadeType.Merge, CascadeType.DETACH, CascadeType.REFRESH})
#JoinTable(
name= "mapping_table",
joinColumns = #JoinColumn(name ='basketId'),
inverseJoinColumns = #JoinColumn(name ='itemId'),
indexes = {#Index(name = "my_index", columnList = "basketId, itemId", unique = true)}
#SQLInsert(sql = "INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX (mapping_table, my_index) */ INTO mapping_table(basketId, itemId) values (?,?)")
private List<Item> itemList;
...
}
#Entity
Class Item{
#Id
private long itemId;
}
my_index with the combination of both the keys are getting created, as expected in the mapping_table
Problem 1:
In the mapping_table, for some wierd reason, a new unique constraint with only itemId is created. Due to this unique key constraint, i am not able to persist rows where an item is associated with multiple baskets. As i said, i want the combination of both the keys to be unique and i am achieving this by creating the index (my_index)
Problem 2:
Why is basketId (which is also Identifier) in the basketTable not marked as unique in the mapping table. This not a problem but more of a question. Becuase itemId which is identifier in the item table has unique key constraint in the mapping table.
Solution :
I create the tables using spring data jpa, login to the db manually and drop the unique key constraint and the persist. But this is not possible in Production
Question
I want to do alter table to drop the constraint first before the persist thing happens. How to do that.
P.S, As you can see, I have imagined these tables and have not put the names of the actual table. Not withstanding the made up table names, the problem statement is accurate. I am using Oracle as target DB
Try to use a Set instead of a List, JPA should generated the correct schema with the correct constraints.

Spring boot JPA how to query a #OneToMany relationship given one object in the many relationship

I've seen a few related questions but I can't seem to find the right answer for what I'm trying to do.
I have two tables Jobs and Workers, a job can have many workers, simplified entity
#Entity
#Table(name = "jobs")
data class Job(
#Id
#Type(type = "pg-uuid")
val id: UUID = UUID.randomUUID()
) {
#ManyToOne
var office: Office? = null
#OneToMany(targetEntity = Worker::class)
var requests: MutableList<Worker> = mutableListOf()
}
I want to be able to fetch a list of jobs for a specific worker
I've tried a few queries native and not, but trying to just do it by namedMethods now, whatever works I guess to be honest here is what seems like it should work in my jobs repo
#Repository
interface JobsRepo : CrudRepository<Job, UUID> {
#Query("SELECT j FROM Job j WHERE id = ?1")
fun findJobById(id: UUID): Job?
#Query("SELECT j FROM Job j WHERE office_id = ?1")
fun findJobsByOffice(id: UUID): List<Job>?
#Modifying
#Transactional
#Query("UPDATE jobs SET job_status = 4 WHERE job_status = 1 AND start_time < ?1", nativeQuery = true)
fun expireJobs(date: Date)
fun findByRequests_Worker(worker: Worker): List<Job>?
}
I'm not really sure how to query the array property
requests
with in input of one worker. I tried querying the UUID of the worker too since thats whats in the join table
JPA creates a join table with both foreign keys the table is
jobs_requests
and columns
job_id UUID
requests_id UUID
You mentioned that a Job can have many workers, and you also mentioned that you want to get a list of jobs for a specific worker. So this sounds like a ManyToMany relationship rather than OneToMany.
For a ManyToMany relationship, a join table is unavoidable. You need to specify #ManyToMany annotation on both entities, then you can use WorkerRepository to query for the worker and then to get the job list for that worker you will just need to access by worker.getJobs().
Following is the setup for the ManyToMany relationship, hope it can help:
#Entity
#Table(name = "jobs")
data class Job (
#ManyToMany
#get:ManyToMany
#get:JoinTable(
name = "worker_job",
joinColumns = [JoinColumn(name = "job_id")],
inverseJoinColumns = [JoinColumn(name = "worker_id")]
)
val worker: Set<Worker> = HashSet()
)
#Entity
#Table(name = "worker")
data class Worker (
#ManyToMany
#get:ManyToMany
#get:JoinTable(
name = "worker_job",
joinColumns = [JoinColumn(name = "worker_id")],
inverseJoinColumns = [JoinColumn(name = "job_id")]
)
val jobs: Set<Jobs> = HashSet()
)
Its also worth noting that for all practical uses, you should try to avoid explicit ManyToMany annotation. You should rather make a new Entity/Table which will have fields of a Job and Worker both with ManyToOne associations to their respective entities.
The reason why you should avoid explicit ManyToMany is because, say when there is a specific job for example JavaDev, and there are say 1000 Workers on the given job. And at one point you have a JavaDev entity and you want to get all the workers whose ages are between 20 and 25. To do this, one would iterate on the Workers list which is part of JavaDev entity and filter them out. When that happens there is 2 scenarios:
Workers list is a lazy association and when you try to access it hibernate will send 1000(n+1 problem) queries to the database.
Workers list is an eager association and hibernate will send same amount of queries to the database but as soon as you try to fetch JavaDev entity.
Hence your execution times will increase as there will be unnecessary amount of load on the database itself. And on larger projects this will get even more complicated.
All of this can be avoided if you create your own manytomany(job_workers for ex.) table, with accompanying repository and a service, from witch you'll be able to query your data more efficiently.

#SecondaryTable with where condition

I am creating entity for table created outside of my system. I want to bind data from other table to entity field by using #SecondaryTable (or possibly better solution), but only to do so if condition is met. IE. my table has 1 row, I want to bind data from other table (oneToMany) where certain condition is met (exactly one match from other table(transform to one to one)). Can I use #Where annotation and how? If not is there alternative?
Edit: here is the entity and additional info on the related table
#Entity
#Table(name = "RE_STORAGE_INSTANCE")
public class Movie {
#Id
#Column(name="ID_")
private Long id;
...
//Column I want to fetch
private Date dueDate;
}
Table RE_VARIABLES manyToOne to table RE_STORAGE_INSTANCE, contains fields: re_key, re_value. I want to fetch re_value only if 're_key' equals dueDate. Even though it's manyToOne, only one row of RE_VARIABLES contains due date for each RE_STORAGE_INSTANCE row.

Returned entity ID is different from the actual data saved in the database

I'm using Spring Data JPA. When I'm inspecting the Entity returned by the save(Entity) method, it is different from the actual ID saved in the database. I'm using Oracle and my Entity ID config is using sequence from Oracle db.
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_NAME")
#SequenceGenerator(name = "SEQ_NAME", sequenceName = "SEQ_1", allocationSize = 1)
private BigDecimal id;
Example scenario:
Insert record1 using save(record1);
Inspected record1 to have ID = 1001
Checked the db and the ID is 1002.
All other data are OK except the ID.
EDIT:
I remove all other logic in the code and leave just the saving part to isolate the issue.
I just found out that there's a db trigger that calls the SEQ1.nextVal every time a record is getting inserted. Dropping the trigger and altering the table to have DEFAULT SEQ1.nextVal solves my problem.

JPA Enitiy with one columns joined in a relationship for two other entities

Currently I'm trying to implement several entities of which one has a column in database which can represent both of other two entities.
To make it clear these are the tables I'm talking about and I can not change them
Images table:
Users table is pretty big, but part of it should be enough:
And pages table:
the logic is that image can be owned by user or by page. who it is owned by indicates by position_id and path columns. so path can be either USER or PAGE, and position_id is user's/page's id.
How can I implement ManyToOne relation between position_id and user and page in JPA?
I imageined it would look something like this:
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="position_id", insertable = false, updatable = false)
#Where(clause="path='pages'")
private Page page;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="position_id", insertable = false, updatable = false)
#Where(clause="path='users'")
private User user;
However this returns all image instances for user or page, disregarding what path it has. Furthermore, when I try to filter the value depending on Image entity's path value, it gives me LazyInitializationException.
How can one go about in solving this problem?

Resources