I hope you can help me on that.
Table A has a multi-column join to Table B where one of the JoinColumns can be nullable...
#Entity
#Table(name = "TABLE_A")
public class TableA {
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumns({
#JoinColumn(name = "KEY1_TABLE_A", referencedColumnName = "KEY1_TABLE_B"),
#JoinColumn(name = "KEY2_TABLE_A", referencedColumnName = "KEY2_TABLE_B"),
#JoinColumn(name = "GROUP_TABLE_A", referencedColumnName = "GROUP_TABLE_B", nullable = true)})
private TableB typeB;
}
In TableB object the columns
TABLE_B#KEY1_TABLE_B (not null)
TABLE_B#KEY2_TABLE_B (not null)
TABLE_B#GROUP_TABLE_B (nullable)
are mapped as Strings.
The touple KEY1_TABLE_B /KEY2_TABLE_B /GROUP_TABLE_B is a unique key.
The generated SQL is as follows (shortened)
SELECT
*
FROM
table_a this_
INNER JOIN table_b b_ ON
this_.KEY1_TABLE_A = b_.KEY1_TABLE_B AND
this_.KEY2_TABLE_A = b_.KEY2_TABLE_B AND
this_.GROUP_TABLE_A = b_.GROUP_TABLE_B <-- here is the issue: works only with "is not null" on Oracle
WHERE
this.XYZ=<some-conditions-here>;
if i would write the SQL directly it should be something like
on ... AND (
(this_.GROUP_TABLE_A = b_.GROUP_TABLE_B)
OR (this_.GROUP_TABLE_A is null and b_.GROUP_TABLE_B is null)
)
Thanks for your thoughts and ideas!
Related
I have three entities: EntityA maps to table_a, EntityB maps to table_b, and Catalog maps to catalog. In the database, there's a many-to-many table between table_b and catalog, b_catalog_xref. EntityB has a field: Long aId, and a field: List<Catalog> catalogs. The Catalog entity has a field: String name. Given a list of IDs for EntityB, and a string representing a catalog name, I need to retrieve all occurrences of EntityA whose ID matches that of an EntityB's aId, and where the given catalog name matches that of one of EntityB's catalogs.
I've successfully grabbed the correct data via regular SQL, but I'm struggling to recreate the query in JPQL. Here's the SQL query:
SQL:
SELECT
*
FROM
table_a a
WHERE
a.table_a_id in (
SELECT
b.table_a_id
FROM
table_b b
INNER JOIN b_catalog_xref bcx ON bcx.table_b_id = b.table_b_id
INNER JOIN catalog c ON c.catalog_id = bcx.catalog_id
WHERE
c.catalog_name = 'Example Catalog Name'
);
Java:
#Entity
#Table(name = "table_a")
public class EntityA {
#Id
#Column(name = "table_a_id")
private Long aId;
...
}
#Entity
#Table(name = "table_b")
public class EntityA {
#Id
#Column(name = "table_b_id")
private Long bId;
#Column(name = "table_a_id")
private Long aId;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH})
#JoinTable(name = "b_catalog_xref",
joinColumns = {#JoinColumn(name = "table_b_id")},
inverseJoinColumns = {#JoinColumn(name = "catalog_id")})
#Fetch(FetchMode.SELECT)
#OrderBy("name ASC")
List<Catalog> catalogs
...
}
#Entity
#Table(name = "catalog")
public class Catalog {
#Id
#Column(name = "catalog_id")
private Long catalogId;
#Column(name = "catalog_name")
private String name;
...
}
Yes, you can use something like
TypedQuery<TableA> q = entityManager.createQuery
("Select a from TableA a where a.aId in(Select b.aId from TableB b " +
"join b.catalogs c where c.name=:name)", TableA.class);
q.setParameter("name", "some2");
I advise you to consider creating a relationship between tables A and B instead of copying the key of table A to table B
#Entity
#Table(name = "table_b")
public class EntityB {
...
// #Column(name = "table_a_id")
// private Long aId;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "table_a_id")
private TableA tableA;
...
}
Then JPQL will look like:
TypedQuery<TableA> q = entityManager.createQuery
("Select distinct b.tableA from TableB b join b.catalogs c " +
"where c.name=:name", TableA.class);
q.setParameter("name", "some2");
Pay attention to the keyword distinct, it removes all duplicates in the result list.
And do not use FetchType.EAGER unless absolutely necessary, use FetchType.LAZY.
#ManyToOne
#JoinColumn(name = "someValue" , referencedColumnName = "someOtherValue" )
What values are to be placed in name and referencedColumnName column if 2 tables are linked by ManyToOne association?
Suppose you have Two tables:
1. Department table with columns:
a. Dept_ID (primary key)
b. Dept_Name
2. Employee Table with following column:
a. Emp_Id (primary key)
b. Dept_Id_fk (foreign key)
c. Salary
Now your join column for Employee Entity class will be
#ManyToOne
#JoinColumn(name = "Dept_Id_fk", referencedColumnName = "Dept_ID")
Department department;
So referencedColumnName means column that you are referencing to from your foreign key column.
How would the join column look like for oneToMany association? Here is an example:
Person table:
person_id (pk), person_name
person_reltn table:
person_reltn_id (pk),
child_person_id (fk),
parent_person_id (fk)
For the above tables, if I were to create the Person entity:
class Person(){
#Id
#Column(name = "PERSON_ID")
private long personId;
#NotFound(action = NotFoundAction.IGNORE)
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(
name = "CHILD_PERSON_ID",
referencedColumnName = "PERSON_ID",
updatable = false,
insertable = false)
#Where(clause = "ACTIVE_IND = 1")
#Filter(
name = FilterConstants.END_EFFECTIVE_DATE_TIME_FILTER,
condition = FilterConstants.END_EFFECTIVE_DATE_TIME_CONDITION)
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private final List<PersonRelation> personRelations = new ArrayList<>();
}
In the joinColumn, should the name always have the foreign key (which means the value from entity you are joining to) and the referenceColumnName should alway have the primary key on the entity? If yes, it will be the opposite of Sayantan's response above. Please let me know if I misunderstood the concept.
Update on 03/04/2021
After doing more research, I found the documentation on how to set the referenceColumnName based on the entity mappings[1]. Looks like, for unidirectional OneToMany mapping, the referenced column is in the table of the source entity.
1.https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/JoinColumn.html
I am trying to persist a OneToMany parent child relation with Join table, but in insert sql for join table only one column is considered (out of two).
insert into ITEM_BIDS_REL (BID_ID) values (?)
It does not include ITEM_ID column and due to that getting below error.
java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("BIDDING_SYSTEM_OWNER"."ITEM_BIDS_REL"."ITEM_ID")
Two question here:
Why it only includes BID_ID column in INSERT query for ITEM_BIDS_REL join table.
Is the right way to design JOIN TABLE like I have kept BID_ID ad PK,FK and ITEM_ID as FK.
Use case:
Referring the "Online Action (bidding) System" example from Java Persistence with Hibernate book.
An Item can have many Bids on it.
ITEM table
ITEM_ID PK
NAME
INITIAL_AMOUNT
ITEM_BIDS table
BID_ID PK
AMOUNT
ITEM_BIDS_REL Join Table
BID_ID PK (FK from ITEM_BIDS table)
ITEM_ID NOT NULL (FK from ITEM table)
Entity classes:
Item Class
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable( name = "ITEM_BIDS_REL",
joinColumns = #JoinColumn(name = "ITEM_ID"),
inverseJoinColumns = #JoinColumn(name = "BID_ID"))
#JsonIgnore
private Set<Bid> bids;
Bid class
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(name = "ITEM_BIDS_REL",
joinColumns = {#JoinColumn(name = "BID_ID", insertable = false,
updatable = false)},
inverseJoinColumns = {#JoinColumn(name="ITEM_ID", insertable = false,
updatable = false)})
private Item item;
Persistence logic:
public Item addBid(BidDTO bidDTO) {
Item item = itemsRepository.findOne(bidDTO.getItemId());
Bid bid = Bid.builder()
.item(item)
.bidAmount(bidDTO.getBidAmount())
.build();
if(item.getBids() == null){
item.setBids(new HashSet<>());
}
item.getBids().add(bid);
bidRepository.save(bid);
itemsRepository.save(item);
return item;
}
I have 2 classes, many-to-many relationship between them. Classes are Client and Role:
#Entity
#Table(name="iq_role")
public class Role extends IdEntity {
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
private Set<Client> clients = new LinkedHashSet<>();
}
and
#Entity
#Table(name="iq_client")
public class Client extends DeletableEntity {
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "iq_client_roles", joinColumns = #JoinColumn(name="iq_client", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name="iq_role", referencedColumnName = "id"))
private Set<Role> roles = new HashSet<>();
}
when I select a specific client and join fetch his roles, this is the SQL (generated by Hibernate):
select
client.id,
role.id
from
iq_client client
left outer join
iq_client_roles client_roles
on client.id = client_roles.iq_client
left outer join
iq_role role
on client_roles.iq_role = role.id
where
client.id=1
in table iq_client I have record with ID 3, in table iq_role I have record with ID 1 and in table iq_client_roles I have record 3,1.
When I copy this generated SQL and run it from console, I get correct result, but hibernate is not able to map the result into objects obviously.
Any idea why?
Given 3 tables:
student (id)
student_to_class (student_id, class_id)
class (id)
I'd like to apply a where clause on the student_to_class where student_id = :studentId. I've found many examples that apply where clause on "class" table or "student" table, but not the many-to-many table.
The student table has a #ManyToMany
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "student_to_class",
joinColumns = { #JoinColumn(name = "student_id", nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "class_id", nullable = false) }
)
private Set<ClassEntity> classes;
The class table has a #ManyToMany
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "student_to_class",
joinColumns = { #JoinColumn(name = "class_id", nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "student_id", nullable = false) }
)
private Set<StudentEntity> students;
Here is the query I'm trying to translate into Criteria:
select * from student, student_to_class where student_to_class.student_id = 1 and student.id = student_to_class.class_id
I'm trying to figure out how to reference the many-to-many table since I don't have an actual class representing this table.
Criteria c = sessionFactory.getCurrentSession().createCriteria(ClassEntity.class);
c.createAlias("student_to_class", "entities"); // how to reference the student_to_class ?
c.add(Restrictions.eq("entities.user_id", studentEntity.getId()));
But I get an error, which makes sense to me, but I haven't had much luck fixing it:
could not resolve property: student_to_class
Since the studentid in the Student table will be the same as the studentid in the student_to_class table there is no need to filter by the join table. Simply run the where clause against student.student_id