I have a problem, that I would like to explain with the following example:
JPA class Person:
#ManyToMany(mappedBy = "persons")
private List<Car> cars= new ArrayList<>();
public void setCars(List<Car> cars) {
this.cars= cars;
}
public List<Car> getCars() {
return cars;
}
and where the problem shows itself:
Servlet class:
Person person = genericDao.findOne(1);
request.setAttribute("person", person.getName());
List<Car> cars= new ArrayList<Car>();
cars= person.getCars();
request.setAttribute("cars", cars);
The problem is that when trying to get the cars. They aren't automatically retrieved from the database and saved in the array in the Person class. How can I setup a JPA class with a Many to Many relationship, from which I can easily retrieve objects with a relationship.
Car.java
IMPORTS LEFT OUT!
#Entity
#Table(name = "DSD_Cars")
public class Car extends ModifiableEntity {
private String name;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#ManyToMany
#JoinTable(
name="DSD_Persons_Cars",
joinColumns = {#JoinColumn(name="IDCar", referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name = "IDPerson", referencedColumnName = "ID")}
)
private List<Person> persons= new ArrayList<>();
public void setPersons(List<Person> persons) {
this.persons= persons;
}
public List<Persons> getPersons() {
return persons;
}}
The problem has been solved:
The Car class had an #Table(name = "DSD_Cars") annotation. However, the #Table used a wrong import rule. After changing the #Table type to the JPA version, everything worked. It seems JPA uses the class name if the #Table isn't defined properly. That explains the error.
Thanks to everyone who spent time to help me!
Related
I am trying to add ManyToMany entity to my application. I created entity but cannot implement it.
Actor class
#Entity
#Table(name = "actor")
public class Actor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false, name = "actor_name")
private String actorName;
#ManyToMany(mappedBy = "actor", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Movie> movie = new HashSet<Movie>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getActorName() { return actorName; }
public void setActorName(String actorName) {
this.actorName = actorName;
}
public Set<Movie> getMovie() {
return movie;
}
public void setMovie(Set<Movie> movie) {
this.movie = movie;
}
}
In movie class I have
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "movie_actor",
joinColumns = {#JoinColumn(name = "movie_id")},
inverseJoinColumns = {#JoinColumn(name = "actor_id")}
)
Set<Actor> actor = new HashSet<Actor>();
........................
public Set<Actor> getActor () {
return actor;
}
public void setActor(Set<Actor> actor){
this.actor = actor;
}
I created my entity just like this but in MovieService;
Actor actor = ActorRepository.findByActorName(movie.getActor().getActorName());
movie.setActor(actor);
This part gives me error. movie.getActor().getActorName() method cannot find. Where do I need to look? In IDE it also says method getActorName and setActorName is never used. I am also adding my ActorRepository and ActorService to closer look to the problem.
ActorRepository
public interface ActorRepository extends JpaRepository<Actor, Integer> {
Set<Actor> findByActorName(String actorName);
}
ActorService
#Service
public class ActorService {
private ActorRepository actorRepository;
#Autowired
public ActorService(ActorRepository actorRepository) {
this.actorRepository = actorRepository;
}
public List<Actor> getAllActor() {
return actorRepository.findAll();
}
}
After adding ManyToMany I was using is as OneToMany entity. Services is works for OneToMany. How can I use them for ManyToMany? I need to add multiple actors to my movies. I couldn't find MVC projects for ManyToMany implementation.
You're invoking movie.getActor().getActorName() which basically does a getActorName() on a Set<Actor> object.
You're basically treating the relation as a ManyToOne instead of a OneToMany
You could use the following to fetch the first Actor of the Set
ActorRepository.findByActorName(movie.getActors().iterator().next().getActorName());
But then of course, you don't have all your Actor's names
What you could do is the following
public interface ActorRepository extends JpaRepository<Actor, Integer> {
Set<Actor> findByActorNameIn(List<String> actorName);
}
And invoke it that way
ActorRepository.findByActorNameIn(
movie.getActors()
.stream()
.map(Actor::getName)
.collect(Collectors.toList())
);
I have two tables below:
#Entity
#Table(name="COLLEGE")
public class College {
private Long collegeId;
private List<Student> students;
#Id
#Column(name = "COLLEGE_ID")
public Long getCollegeId() {
return this.collegeId;
}
public void setCollegeId(final Long collegeId) {
this.collegeId= collegeId;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "college")
#Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
public List<Student> getStudents() {
if (this.students== null) {
this.students= new ArrayList<>();
}
return this.students;
}
public void setStudents(List<Student> students){
this.students = students
}
}
#Entity
#Table(name="STUDENT")
public class Student {
private Long studentId;
private College college;
private String name;
private String department;
#Id
public Long getStudentId() {
return this.studentId;
}
public void setStudentId(Long studentId) {
this.studentId = studentId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COLLEGE_ID")
public getCollege() {
return this.college;
}
public setCollege(College college) {
this.college = college;
}
......
......
}
I want to get the college object with list of students of a specific department. The SQL query is below:
select *
from COLLEGE c
inner join STUDENT s on c.COLLEGE_ID= s.collegeId
where c.COLLEGE_ID=12345 and s.DEPARTMENT="ENGINEERING";
So far I have tried the below JPA query but it is returning multiple College objects.
SELECT DISTINCT c
FROM COLLEGE c
INNER JOIN c.students s
where c.collegeId= :collegeid and s.department = :department
How to return a single college object with list of students with filtered department?
NOTE: I can't alter the entity objects used here.
Try to use this query, using JOIN FETCH instead INNER JOIN:
SELECT DISTINCT c FROM College c
JOIN FETCH c.students s
WHERE c.collegeId= :collegeid
AND s.department = :department
I've 2 tables Employee & Vehicle, where one employee can have multiple vehicles.
Below is the mapping that I've defined:
Employee.java
#Entity(name = "emp_details")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int empId;
#OneToMany(cascade = CascadeType.ALL, mappedBy="employee")
private List<Vehicle> vehicles = new ArrayList<>();
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public List<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(List<Vehicle> vehicles) {
this.vehicles = vehicles;
}
}
Vehicle.java
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int vehicleId;
#ManyToOne
#JoinColumn(name="empId")
private Employee employee;
private String name;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public int getVehicleId() {
return vehicleId;
}
public void setVehicleId(int vehicleId) {
this.vehicleId = vehicleId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Main class
public class HibernateTest {
public static void main(String[] args) {
Employee emp = new Employee();
Vehicle vehicle = new Vehicle();
vehicle.setName("Honda");
emp.getVehicles().add(vehicle);
SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
Session session = sFactory.openSession();
session.beginTransaction();
session.save(emp);
session.getTransaction().commit();
session.close();
StandardServiceRegistryBuilder.destroy(sFactory.getSessionFactoryOptions().getServiceRegistry());
}
}
But when I execute this, Vehicle.employee_id is empty. I was expecting that my foreign key will be inserted there.
What am I missing?
Thank You
You need to show us the code that persists your entities, but my guess is that you are not setting the employee to the vehicle.
You need to manage both sides of bidirectional entity relationship. Your code should look something like this
employee.getVehicles().add(vehicle);
vehicle.setEmployee(employee);
session.save(employee);
UPDATE:
In this case, Vehicle is the owning side of the relation because the foreign key is in its database table. You just added the new vehicle to the employee's list of vehicles. When you save the employee, there's nothing to change in Employees database, and the save operation cascades to Vehicle. Vehicle does not have its employee set, it is null, so it puts null in empId column
Bottom line, you have to make sure both sides of bidirectional relationship are wired up correctly.
If you have a bidirectional relationship you have to
set the relation on both sides.
That means you have to set the employee for your vehicle also.
You can do this by calling
vehicle.setEmployee(emp);
and then store or update your entity (if it's not attached to session already).
Usually to set a bidirectional relationship you provide special methods in your entities.
public class Vehicle {
...
public void setEmployee(Employee employee) {
this.employee = employee;
employee.addVehicle(this)
}
...
}
public class Employee {
...
public void addVehicle(Vehicle v) {
if(!vehicles.contains(v)) {
vehicles.add(v);
}
if(!this.equals(v.getEmployee()) {
v.setEmployee(this);
}
}
...
}
In your Employee entity class, you haven't used #Column annotation on empId. In Vehicle class you are referencing Employee using #JoinColumn(name="employee_id"),so column employee_id must exist in emp_details table. So you need to modify your Employee class to something with
#Id
#GeneratedValue
#Column(name="employee_id")
private int empId;
I think need to edit your code. you miss the entity jpa rule. you can some search jpa entity. you read description for you. link : enter link description here
(Modified your Employee.java)
#Entity(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int idx;
#OneToMany(cascade = {CascadeType.ALL}, mappedBy = "employee")
private List<Vehicle> vehicle = new ArrayList<>();
...
}
(Modified your Vehicle.java)
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int idx;
#ManyToOne
#JoinColumn(name="employee_id")
private Employee employee;
...
}
You only need to cascade onetoMany from Employee Entity
I have two classes Game and Team which have many-to-many relationship between them.
Game:
#Entity
#Table(name = "GAME")
public class Game extends Model {
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="GAME_TEAM",
joinColumns={#JoinColumn(name="GAME_ID")},
inverseJoinColumns={#JoinColumn(name="TEAM_ID")})
private List<Team> teams = new ArrayList<Team>();
public List<Team> getTeams() {
return teams;
}
public void setTeams(List<Team> teams) {
this.teams = teams;
}
}
Team:
#Entity
#Table(name = "TEAM")
public class Team extends Model {
#Column(name = "TEAM_NAME")
private String teamName;
#OneToMany(mappedBy = "team", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Player> player = new LinkedHashSet<Player>();
#ManyToMany(cascade=CascadeType.ALL, mappedBy="teams")
private Set<Game> games;
public Set<Player> getPlayer() {
return player;
}
public void setPlayer(Set<Player> player) {
this.player = player;
}
public Set<Game> getGames()
{
return games;
}
public void setGames(Set<Game> games) {
this.games = games;
}
public String getTeamName() {
return teamName;
}
public void setTeamName(String teamName) {
this.teamName = teamName;
}
There is a third intermediate table which between these two classes GAME_TEAM which has the team_id, game_id which are foreign keys to the ids from game and team class. So whenever I am updating team and game, I automatically update the game_team.
Ok. Good. Now what is the problem? I have a third column WON_BY which also has to be updated. Without this additional third column, I did not have to create the third class GameTeam.
To update the third column which is not a foreign key, should I create an entity class for the intermediate table? If so what kind of approach or annotation should I use?
You need to have a separate entity class to persist your 'WON_BY', and
you need to re-factor your existing entity classes a bit, since the
solution is quite big refer the below sample example, which will
definitely solves your problem.
http://www.codejava.net/frameworks/hibernate/hibernate-many-to-many-association-with-extra-columns-in-join-table-example
I'm having some trouble designing a query in a CrudRepository.
I have two entities, CourseOffering and Department (only relevant code shown):
CourseOffering.java:
public class CourseOffering implements Serializable
{
private Department department;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "DepartmentId", nullable = true)
#JsonProperty
public Department getDepartment()
{
return this.department;
}
public void setDepartment(Department department)
{
this.department = department;
}
}
Department.java:
public class Department implements Serializable
{
private Set<CourseOffering> courses;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "department")
public Set<CourseOffering> getCourses() {
return this.courses;
}
public void setCourses(Set<CourseOffering> courses) {
this.courses = courses;
}
}
and the CrudRepository in question:
CourseOfferingRepository.java:
import java.util.List;
import edu.ucdavis.dss.dw.entities.CourseOffering;
import org.springframework.data.repository.CrudRepository;
public interface CourseOfferingRepository extends CrudRepository<CourseOffering, Long>
{
CourseOffering getOneByTermIdAndNumberAndDepartmentId(long termId, String number,
long departmentId);
List<CourseOffering> findByDepartmentCode(String deptCode);
//List<CourseOffering> findAllByDepartmentCode(String deptCodes);
List<CourseOffering> findByTermCode(String termCode);
}
The three functions in CourseOfferingRepository which are not commented out work as expected. I am trying to get the fourth to work.
What I'd like to do is be able to return all CourseOfferings where the department code is one of many department codes. Note that the CourseOffering table itself only holds a department_id integer which references the ID in the Department table, where the actual deptCode is stored.
How would I go about getting that commented out CrudRepository function to work properly? Or put another way, how does one make the plural version of "List findByDepartmentCode(String deptCode);"?
Thanks in advance for any advice you can offer.
You need to change the commented out code to:
List<CourseOffering> findByDeptCodeIn(Collection<String> deptCodes)
Check out this part of the documentation to see what other keywords are allowed
As geoand pointed out in the comments, the answer is:
List<CourseOffering> findByDepartmentCodeIn(List<String> deptCodes);
Thanks geoand!