I'm trying to create simple select query with HQL.
There is an Entity that will be used in query. It Looks like the following:
#Entity
#Table(name = "my_table")
public class MyTable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "test_id")
private Long testId;
#Column(name = "parent_id")
private Long parentId;
#OneToMany(mappedBy = "parentId",
fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
private Set<MyTable> children = new HashSet<MyTable>();
//getters and setters
}
Hierarchy is simple. There are Parents (which have null parent_id value) and their children. So two levels only.
I would like to create query that selects all parents and their children, but there is a condition for children: it should equal to specific test_id. E.g. it is required to have children only with test_id = 1. Table consists of Parent1 with Child1 (test_id = 2) and Parent2 with Child2 (test_id = 1). The query result should be Parent1 without children and Parent2 with Child2.
Query:
from MyTable as myTable left fetch join myTable.children as child
where child.testId = 1
As a result - I'm getting only those parents which have children with "1" test_id. But I need all parents to see even if there no needed children.
What is wrong here: mapping or query? And how should it actually be?
Thanks in advance.
Your mapping is wrong. You have a unidirectional OneToMany association. SO you obviously must tell Hibernate how this association is mapped. And what you tell is:
#OneToMany(mappedBy = "parentId", ...)
This means: "Go look at the other side of this bidirectional association to find how this association is mapped. I'm only the inverse side".
But there is no other side. Your association is unidirectional.
The mapping should be:
#Entity
#Table(name = "my_table")
public class MyTable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "test_id")
private Long testId;
#OneToMany(fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
#JoinColumn(name = "parentId")
private Set<MyTable> children = new HashSet<MyTable>();
//getters and setters
}
Note that the parentId field has been removed, since it's already used to map the children association.
Another problem is your expectations. A query returns columns, or it returns entities. If it returns a parent entity and you ask this entity for its children, all the children will be returned. AN entity represents what is in the database. It doesn't represent the result of a specific query.
What you could do, if you only want to have some of the children, is to search for children, and get their parent. To do that, you would need to make the association bidirectional:
#Entity
#Table(name = "my_table")
public class MyTable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "test_id")
private Long testId;
#ManyToOne
#JoinColumn(name="parentId")
private MyTable parent;
#OneToMany(mappedBy="parent",
fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
#JoinColumn(name = "parentId")
private Set<MyTable> children = new HashSet<MyTable>();
//getters and setters
}
And the query would be:
select child from MyTable child
left join fetch child.parent
where child.testId = 1
This would return all the children with testId = 1, along with their parent. You would need a second query to get all the other parents. Something like
select parent from MyTable parent
where parent.id not in (
select parent2.id from MyTable child
left join child.parent parent2
where child.testId = 1)
Related
I have a parent table, and I am trying to create a #OneToMany relationship between the parent and child tables (e.g. one parent table can be associated with several child tables).
Parent.java
#Entity
#Table(name = "parent")
public class Threat {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long parentId;
#Column(name = "name")
private String parentName;
}
Child.java
#Entity
#Table(name = "child")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long childId;
#Column(name = "parent_id")
private long parentId;
#Column(name = "name")
private String childName;
#Column(name = "age")
private Integer childAge;
}
I am having trouble figuring out how to do this given the fact I need to join the primary key of the parent table (parent.id) to a column in the child table that is not the primary key (child.parent_id). Ideally, when I return a Parent object, I would like both the parent id and the list of children displayed, so the parent id is listed in both the parent and children. That way, if the parent has no children, I still have the parent id in my object.
I have been doing some research, and I'm wondering if I should be using
#PrimaryKeyJoinColumn
and/or
#Inheritance(strategy = InheritanceType.JOINED)
I haven't been able to figure out how to implement them though. Any help would be greatly appreciated.
Checkout #JoinColumn.
From Java doc :
Specifies a column for joining an entity association or element collection. If the JoinColumn annotation itself is defaulted, a single join column is assumed and the default values apply.
Example:
#ManyToOne
#JoinColumn(name="ADDR_ID")
public Address getAddress() { return address; }
Example: unidirectional one-to-many association using a foreign key
mapping
// In Customer class
#OneToMany
#JoinColumn(name="CUST_ID") // join column is in table for Order
public Set<Order> getOrders() {return orders;}
With this your entity should look like the following :
#Entity
#Table(name = "parent")
public class Threat {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long parentId;
#Column(name = "name")
private String parentName;
#OneToMany
#JoinColumn(name="parent_id")
private List<Child> children = new ArrayList<>();
}
Now whenever you fetch Parent , you can access the associated child entities.
Additionally you can specify #OneToMany(fetch = FetchType.EAGER) if you always need child entities fetched whenever you query for parent.
I am trying to stop my relationship making new tables. I have tried multiple approaches to this problem, but there seems to be an error every way I turn. For instance when I try the following code:
//other variables
#OneToMany(fetch = FetchType.LAZY ,cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private List<User> users= new ArrayList<>();
I get the following error:
Caused by: java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`eb322`.`#sql-3140_2e7`, CONSTRAINT `FK20sqpkpotyyf5wx4jfmp519lu` FOREIGN KEY (`user_id`) REFERENCES `year` (`year_id`))
I have checked all my tables and indexes in the database and I cannot find this constraint anywhere. How do I go about removing it. I basically want to have my schema be like this:
Year will have a list of all students, teachers. When a student is enrolled they will be added to that year etc.
If I don't add the join Column I simply get another table saying
Year.students
How do I combine these together.
This is my student class just incase there's something wrong here:
public class Student{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int User_id;
}
How I am adding data to year table
//get data about student
Student s = ssrepo.findByName(name);
Year y = yyrepo.findByYear(year);
List<Student> students = y.getStudents();
students.add(s);
yyrepo.save(y)
You seem to be using Unidirectional OneToMany relationship
Hibernate uses an association table to map the relationship so when you remove #JoinColumn annotation an association table is created.
As Year has one to many relationship with student, the type of the List should be List<Student> instead of List<User>
#OneToMany(fetch = FetchType.LAZY ,cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private List<Student> users= new ArrayList<>();
And using OneToMany Unidirectional association is normally not recommended because of its performance issues. You can consider using bidirectional association. It would be something as follows
public class Year {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "YEAR_ID")
private Long id;
#Column(name = "TYPE_ID")
private Long typeId
#Column(name = "TYPE")
private Boolean type // 1 or 0 to know if typeId is of student or teacher
#Column(name = "YEAR")
private Date year
#OneToMany(mappedBy="typeId", fetch = FetchType.LAZY ,cascade = CascadeType.ALL)
private List<Student> students;
#OneToMany(mappedBy="typeId", fetch = FetchType.LAZY ,cascade = CascadeType.ALL)
private List<Teacher> teachers;
}
public class Teacher{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "TEACHER_ID")
private Long id;
#ManyToOne
#JoinColumn(name="TYPE_ID", nullable=false)
private Year typeId;
}
public class Student{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "STUDENT_ID")
private Long id;
#ManyToOne
#JoinColumn(name="TYPE_ID", nullable=false)
private Year typeId;
}
There are two ways to do this. The first is bidirectional. Where you do the mapping in the two entities. here in this link.(https://dzone.com/articles/introduction-to-spring-data-jpa-part-4-bidirection)
hava exemples.
public class MyClass {
#OneToMany(mappedBy = "myClass", fetch = FetchType.LAZY,
cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "user_id")
private List<User> users;
}
mappedBy is to say who is the dominate in the relationship. In this case, MyClass has the strongest relationship.
public class Student{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#ManyToOne
#JoinColumn(name = "user_id")
private MyClass myClass;
}
I believe that this is the best way, because her realities are apparent in both entities. There is a way to do it in a unidirectional way. Exemple in link (How to define unidirectional OneToMany relationship in JPA)
I have a unidirectional, one-to-many, parent/child relation.
In my test case, I have 1 parent with 2 children which are inserted via cascading insert.
Looking at the queries that are ran, I have 1 insert for the parent, 1 insert and two update queries for each of the children. The updates for the foreign key - they are setting the parent_id column in the child table, but I can see that the parent_id has already been set correctly by the insert.
Here is an example
#Entity
#Table(name = "PARENT")
public class Parent
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long parentId;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "parent_id", nullable=false)
private List<Child> children;
}
#Entity
#Table(name = "CHILD")
public class Child
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "PARENT_ID")
private Long parentId;
//some other field
}
//The test looks like this
Parent parent = new Parent();
Child child1 = new Child();
Child child2 = new Child();
//set all fields
parent.addChild(child1);
parent.addChild(child2);
em.merge(parent);
Is it possible to not have the update queries?
Is it possible to insert all children in a single query?
You might try persist instead of merge
JPA EntityManager: Why use persist() over merge()?
No. It is not possible. because after insert in parent table, a child table can be inserted.
I'm using hibernate-core:3.3.1.GA
I have three mappings:
class PlayerAccount:
#Entity
#Table(name = "player_account")
public class PlayerAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(targetEntity = Player.class, fetch = FetchType.EAGER)
#JoinColumn(name="player_id")
private Player player;
//GET, SET
}
class PlayerAchievements:
#Entity
#Table(name="player_achievement")
public class PlayerAchievement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(targetEntity = Player.class, fetch = FetchType.EAGER)
#JoinColumn(name = "player_id")
private Player player;
//GET, SET
}
and class Player
#Entity
#Table(name = "player")
#Inheritance(strategy = InheritanceType.JOINED)
public class Player {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "ps_id")
private String psId;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
//GET, SET
}
Issue:
When I try to write a simple criteria query like the following:
Criteria criteria = getSession().createCriteria(PlayerAccount.class);
criteria.add(Restrictions.eq("playerId", playerId));
criteria.list();
The resulting sql-query contains joins have the following view:
SELECT
-- columns to select
FROM player_account this_
LEFT OUTER JOIN player player2_
ON this_.player_id=player2_.id
LEFT OUTER JOIN external_partner_player player2_1_
ON player2_.id=player2_1_.player_id
LEFT OUTER JOIN player_achievement achievemen3_
ON player2_.id=achievemen3_.player_id
WHERE player_id = 2362189
The thing is player_achievements may contain more than one row with the same player_id value. Since we probably have a duplicated result in that criteria query. How to fix that using hibernate whithout the writing an sql-query?
Because the Fetch Types are eager. That means that you always want the entities loaded when you load the main entity.
You can use FetchType.LAZY for best perform. Here explains better: https://howtoprogramwithjava.com/hibernate-eager-vs-lazy-fetch-type/
You need to do SELECT DISTINCT, like this:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
I want to map two entities in a one to many fashion.
A->[B, B]
I want to add to the join table more fields. Pojos looks like:
#Entity
#Table(name = "A", schema = "examples")
#SecondaryTable(name = "A_B", pkJoinColumns = #PrimaryKeyJoinColumn(name = "a_id", referencedColumnName = "id"))
public class A
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Basic
private String name;
#Basic
private Integer field1;
#Column(table = "A_B", name = "field2")
private Integer field2;
#OneToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "A_B", joinColumns = {#JoinColumn(name = "a_id")}, inverseJoinColumns = {#JoinColumn(name = "b_id")})
private List<B> datastores;
}
#Entity
#Table(name = "B", schema = "examples")
#SecondaryTable(name = "A_B", pkJoinColumns = #PrimaryKeyJoinColumn(name = "b_id", referencedColumnName = "id"))
public class B
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Basic
private String field1;
#Basic
private int field2;
#Column(table = "A_B", name = "field3")
private int field3;
}
Thing is that in order to add I had to remove the foreign key on A_B table. How do I solve the mapping to allow the foreign keys ?
Thanks.
I am missing something, but I don't see why both Entity A and Entity B are mapping to table "A_B". By adding it to Entity A as a secondary table, you are stating that every time an insert to Table a occurs, an insert to table A_B must also occur - creating a strict 1:1 relation between rows in the two tables. Except that you do the same thing to entity B, so you will end up with rows in A_B with A_id=somevalue, and B_id= null and others with a_id=null while b_id=somevalue. Table "A_B" looks like a relation table, so this probably isn't what you want.
If A_B is a relationtable you should map it using a ManyToMany as you have for the "A_B" table. If there are extra fields that need to be populated, create a AB Entity, and create a OneToMany from A->AB and B->AB, and ManyToOne from AB->A and AB->B.