This is my schema:
event_details:
- id (PK)
- name
- description
event_ticket_types:
- id (PK)
- event_id (References id (event_details)
- ticket_name
EventDetail
//bi-directional many-to-one association to EventTicketType
#OneToMany(mappedBy="eventDetail", cascade = CascadeType.ALL)
private Set<EventTicketType> eventTicketTypes;
in my EventDetail entity, I have added #Where(clause = "deleted_at is NULL")
I've also added the same in EventTicketType
This is how I am getting the data in service.
EventDetail eventDetail = eventDetailRepository.getById(eventId);
Set<EventTicketType> eventTicketTypes = eventDetail.getEventTicketTypes();
And then I am mapping entity and dto.
In the query log - I see that parent table (event_details) executes query with where condition i.e where deleted_at is NULL, but the child table (event_ticket_types) does not have a where condition
Where is it that I am doing wrong?
You're not showing where the #Where annotations are but I'm guessing they're both at class level only. Try also adding the #Where annotation to the #OneToMany method above.
The annotation works at entity level when you select the entity directly, but you need it on the relationship method also if you want it to work for joins.
Related
I am having a problem with a OneToOne relationship.
Trying to load a children entity will always result in an additional query that loads its parent, even if I don't need the parent.
I have already read to articles about it but can't figure it out:
https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
https://thorben-janssen.com/hibernate-tip-lazy-loading-one-to-one/
My structure is quite similiar to their examples above. I've got a parent entity with a OneToOne relationship to a childen. My mapping is already unidirectional. So only the children has the mapping information and the parent has no children mappings at all. I am using the #MapsId annotation and have a shared PK/FK to the parent.
Short information about my entity structure:
#Entity
#Table(name = "parent")
public class ParentEntity
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ParentSequence")
private Long id;
#Entity
#Table(name = "children")
public class ChildrenEntity
#Id
private Long id;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parent_id")
private ParentEntity parent;
My issue is, that the children always fetches the parent eager and creating a second query for that reason.
Examples:
#1 via Entity Manager find
entityManager.find(ChildrenEntity.class, parentId);
#2 via JPA Query
entityManager.createQuery("SELECT c FROM ChildrenEntity c").getResultList()
#3 via Criteria API
var query = cb.createQuery(ChildrenEntity.class);
var root = query.from(ChildrenEntity.class);
query.select(root).where(root.get(ChildrenEntity_.id).in(Arrays.asList(parent.getId())));
entityManager.createQuery(query).getResultList();
All three examples create more then one query. The additional query always load the whole parent entity.
Here a slightly cleaned up server output:
[0m[0m14:40:29,411 INFO Hibernate: select childrenentity0_.parent_id, childrenentity0_.color, from children childrenentity0_
[0m[0m14:40:29,413 INFO Hibernate: select parententity0_.id, parententity0_.additionalname from parent parententity0_ where parententity0_.id=?
Does anyone has an idea how to prevent loading of the parent entity? I do not really need it as the id is more then enough here.
I already found out, that I can use a .fetch statement in the criteria api. That helps reducing is to one query but that will add all parent fields to the select of the first query.
-- EDIT 22/05/09 --
Related Stackoverflow entry: JPA/Hibernate double select on OneToOne Find
Currently I only could find two possible solutions:
#1 Use a join fetch on ChildrenEntity to fetch the ParentEntity
This would work but joins a lot additional columns that are unneeded
#2 Do not select Entities but use a Tuple/Constructor query
-- EDIT 22/05/11 --
Looks like there is also a bug in various hibernate versions where the MapsId annotation does not work as expected.
https://hibernate.atlassian.net/browse/HHH-10771
I have 2 entities (User , Teacher) the User is the father ...
When I want to remove user by native query it give me this error ( Cannot delete or update a parent row: a foreign key constraint fail )
Though I have written:
#OneToOne(mappedBy = "user_id",fetch = FetchType.EAGER,
cascade = CascadeType.ALL , orphanRemoval = true )
at the Teacher reference in the user entity
but when I try to use JPA derived method deleteByEmail() .. it worked!
What is the reason?
Cascading doesn't work with queries.
It works when you change the state of the entities using the methods in the EntityManager or Hibernate ORM Session.
You can see a list of examples in the Hibernate ORM documentation:
5.15. Cascading entity state transitions.
I am new to Hibernate. I have a OneToMany relationship with bidirectional mapping between Account and Transaction. I am not using #JoinColumn in either class and using #mappedBy in the non owning Account class. And everything is working fine. Using H2 in memory database, new join column is created in Transaction table. Then what is the use of #JoinColumn in OneToMany relationships? Is it for unidirectional mapping only? Below is the code. I also read for reference JPA JoinColumn vs mappedBy
public class Account {
#OneToMany( mappedBy="account", cascade=CascadeType.ALL)
List<Transaction> list= new ArrayList<Transaction>();
}
public class Transaction {
#ManyToOne
Account account;
}
Application class :
Account a = new Account("savings");
Transaction t1 = new Transaction("shoe purchase", 45);
t1.setAccount(a);
a.getList().add(t1);
accountRepository.save(a);
output:
Transaction table has an entry with foreign key which is account number in that one row in Account table. ACCOUNT_ID column in created in Transaction table.
There are no extra tables created.
Jpa works on the idea of configuration by convention. So, it will perform configuration on your behalf whenever it can. Think of the #Column annotation, you don't have to apply it on every entity attribute, you would need it only when you have to change something about the attributes.
It's the same with #JoinColumn, when you added #ManyToOne, Jpa already knows that you will need the join column and thus was added for you and the default naming convention for the foreign key was applied (attributename_primarykeyoftheothertype).
Use of
mappedBy
is instruct framework to enable bi-directional relationship. Because of #ManyToOne on Transaction class you Your Transaction Table will have foreign key referring to Account table primary key. By default, Hibernate generates the name of the foreign key column based on the name of the relationship mapping attribute and the name of the primary key attribute. In this example, Hibernate would use a column with the name account_id to store the foreign key to the Account entity.
#JoinColum
can be used If you would like override default foreign key name like #JoinColum(name="acc_id")
MappedBy intructs Hibernate that the key used for the association is on the other side of the association.Like in this case ACCOUNT_ID is being created on Account table.
That means that although you associate two tables together, only one table is having foreign key constraint to the other one.
MappedBylets you to still associate from the table not having foreign key constraint to the other table.
There is a one-to-many relationship in my model, where the child entity is stored in two tables.
#Entity
#Table(name = "child1")
#SecondaryTable(name = "child2", pkJoinColumns = {
#PrimaryKeyJoinColumn(name = "id1", referencedColumnName = "id1"),
#PrimaryKeyJoinColumn(name = "id2", referencedColumnName = "id2")})
#Where(clause = "col1 is not null and col2 is not null")
#Data
#Immutable
public class Child implements Serializable {...}
Child entity is fetched eagerly together with Parent entity. Problem lies within #Where clause, which should reference columns from two tables: col1 is in table child1 and col2 is in child2. This throws the following error:
ERROR 12333 --- [nio-8183-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : Column (col2) not found in any table in the query (or SLV is undefined).
java.sql.SQLException: null
...
Using only: #Where(clause = "col1 is not null") gives propper mapping and results in no error.
Using #Where(clause = "child1.col1 is not null and child2.col2 is not null") gives the following error:
Column (child1) not found in any table in the query (or SLV is undefined).
How can I make #Where work with two tables or is there any workaround?
There are some requirements though:
I'm using informix as an underlying database and have read-only access.
I know, that it can be solved by native SQL or even JPQL / criteria API and so on, but doing so would make me rewrite a lot of core. I want to avoid it.
This is due to the HHH-4246 issue.
A workaround would be to replace the #SecondaryTable with a #OneToOne association using #MapsId.
This way, the child2 table becomes the Child2 entity for which you can use #Where or #Filter.
I've got the following entity:
#Entity
#Table(name = "ONE")
#SecondaryTable(name = "VIEW_TWO", pkJoinColumns = #PrimaryKeyJoinColumn(name="ONE_ID"))
public class CpBracket {
#Id
private Long id;
#Column(name="progress", table="VIEW_TWO", updatable = false, insertable = false)
private int progress = 0;
(...)
}
As you see, this entity uses table ONE and (read only) view VIEW_TWO. When I'm persisting the entity, hibernate is performing insert into view:
insert into VIEW_TWO (ONE_ID) values (?)
It is ignoring the non-updatable and non-insertable column progress (that's good) and it is still trying to insert value of ONE_ID column. As far as I know, the annotation #PrimaryKeyJoinColumn marks selected column as insertable=false and updatable=false.
How can I prevent hibernate from inserting rows into secondary table (view)?
As far as I know, the annotation #PrimaryKeyJoinColumn marks selected
column as insertable=false and updatable=false.
I do not believe this can be the case: how then do we get records inserted into the #SecondaryTable when it is an actual table rather than a view?
As neither #SecondaryTable or #PrimarykeyJoinColumn have a means to prevent insert then it would appear that your original solution is not going to work and an alternative is required.
One option is to map VIEW_TWO as an #Entity and link to your class CPBracket as a #OneToOne relationship with cascade options set to none.
#Entity
#Table(name ="VIEW_TWO")
private CpBracketSummaryData(){
}
#Entity
#Table(name = "ONE")
public class CpBracket {
#OneToOne
#PrimaryKeyJoinColumn
private CPBracketSummaryData summaryData;
public int getSomeValue(){
return summaryData.getSomeValue();
}
}
The second option would be to use the non JPA compliant, Hibernate specific #Formula annotation.
#Entity
#Table(name = "ONE")
public class CpBracket {
#Formula("native sql query")
private int someValue;
}
Update October 2016
I have revisited this in both Hibernate 4.3.10.Final and 5.1.0.Final and it is possible to have the view as a #SecondaryTable without the insert: if you have the correct mappings.
Scenario 1
Load an entity for edit and do not touch any fields mapped to the secondary table. No update is issued to the secondary table
Scenario 2
Create and save a new entity and do not set any fields mapped to the secondary table. No insert is issued for the secondary table
Scenario 3
Create or update an entity including a field mapped to a secondary table and where this field is marked as insertable = false and updateable = false. An insert is made to the secondary table only for the ID field -the behaviour reported in the original question.
The issue with the mapping in the original question is the fact that the secondary table field is a primitive type and therefore when saving a new entity Hibernate does think a record has to be written to the secondary table with a value of zero.
#Column(name="progress", table="VIEW_TWO", updatable = false, insertable = false)
private int progress = 0;
The solution then is to replace primitives with the corresponding wrapper types and leave them as null. Then when saving a new record there is nothing to write to the secondary table and no insert will be made:
#Column(name="progress", table="VIEW_TWO")
private Integer progress;
I solved a similar problem with #SecondaryTable, which was a database view. So maybe it will help someone else.
The problem was on cascade delete to #SecondaryTable, when record from primary table was deleted.
As a solution, I implemented RULE on view for delete
CREATE RULE on_delete AS ON DELETE TO my_view DO INSTEAD(
select 1;
)
Similar solution can be used for INSERT and UPDATE operation on view.