I am trying to create two entities (student, university) with a unidirectional #ManyToOne relationship between them. University can have many students.
I don't want to save them seperately, i want to save student and university should be saved because of #Cascade.
During saving second student i get exception:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
I don't know how to resolve that problem.
My code:
#Entity
public class Student {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToOne(cascade=CascadeType.ALL)
private University university;
public Student(String name, University university) {
this.name = name;
this.university = university;
}
public Student() {
}
}
#Entity
public class University {
#Id
private Long id;
private String name;
public University(Long id, String name) {
this.id = id;
this.name = name;
}
public University() {
}
}
#Repository
public interface StudentRepository extends JpaRepository<Student,Long> {
}
#SpringBootApplication
public class App implements CommandLineRunner
{
#Autowired
private StudentRepository studentRepository;
#Autowired
private UniversityRepository universityRepository;
public static void main( String[] args )
{
SpringApplication.run(App.class);
}
#Override
public void run(String... strings) throws Exception {
Student student = new Student("pawel", new University(1L,"pw"));
Student student1 = new Student("gawel", new University(1L,"pw"));
studentRepository. save(student);
studentRepository.save(student1);
}
}
I explored that if beside university reference i set student id manually than everything works.
code that works:
#Entity
public class Student {
#Id
private Long id;
private String nazwa;
#ManyToOne(cascade=CascadeType.ALL)
private University university;
public Student(Long id,String nazwa, University university) {
this.id=id;
this.nazwa = nazwa;
this.university = university;
}
public Student() {
}
public Student(String nazwa) {
this.nazwa = nazwa;
}
}
#Entity
public class University {
#Id
private Long id;
private String name;
public University(Long id, String name) {
this.id = id;
this.name = name;
}
public University() {
}
}
#Repository
public interface StudentRepository extends JpaRepository<Student,Long> {
}
#SpringBootApplication
public class App implements CommandLineRunner
{
#Autowired
private StudentRepository studentRepository;
public static void main( String[] args )
{
SpringApplication.run(App.class);
}
#Override
public void run(String... strings) throws Exception {
Student student = new Student(1L,"pawel", new University(1L,"pw"));
Student student1 = new Student(2L,"gawel", new University(1L,"pw"));
studentRepository. save(student);
studentRepository.save(student1);
}
}
Why that code with manual setting of student id works? Why there is a problem with code when i use #generatedValue?
#Result
The difference was because when #GeneratedValue is used, hibernate create queries of save, comparatively when i was assigning id manually, hibernate first check if entity exist in database if yes, entity gets updated if not hibernate save it for the first time.
You have no sequence generator defined. You can do it in your database, then simply use (assuming its name is "my_seq"):
#SequenceGenerator(name="seq",sequenceName="my_seq")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
I can see, that you're using MySQL, then you can use the identity generation method:
#GeneratedValue(strategy = GenerationType.IDENTITY)
together with auto_increment in your id column definition:
create table student(
id int not null auto_increment,
...
)
Edit:
Try saving the university first. Set this as body of your run method:
University uni = new University(1L,"pw");
University savedUni = universityRepository.save(uni);
Student student = new Student("pawel", savedUni);
Student student1 = new Student("gawel", savedUni);
studentRepository.save(student);
studentRepository.save(student1);
If you are saving only Students everyone with the new instance of University, then Students entity are cascading and inserting that instance every time, so that constraints are violated. This happens, because new University(1L,"pw") is not managed and hibernate treats it as a new entity - and because you did not provide an id - it was set as some default (0L). Using University savedUni = universityRepository.save(uni); makes hibernate recognize the entity, so that no additional insert is done.
Related
I have just started experimenting with Neo4J/Java and expect this is an easy one I'm missing, and probably phrasing my queries wrong.
I have some model classes as follows:
#Node
public class Garment {
#Id
#GeneratedValue
private Long id;
private String name;
#Relationship(type = "DESIGNED_BY")
private Entity designer;
// Other properties getters/setters removed for readibility
public Entity getDesigner() {
return designer;
}
public void setDesigner(Entity designer) {
this.designer = designer;
}
}
public abstract class Entity {
#Id
#GeneratedValue
Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Node
public class Person extends Entity {
}
#Node
public class Company extends Entity {
}
And a corresponding repository
public interface Garment extends Neo4jRepository<Garment, Long> {
Garment findByName(String name);
}
I have no problem inserting, using repository.save(), this correctly adds everything; nodes, relationships. Fine. It gives Designers of type Person labels of Person, and Designers of type Company the label Company.
However, when I do a find, e.g. findByName(), findAll(). it is not matching the designer and just saying designer is null, according to the cipher being executed/logged it looks like it's trying to build a relationship there with nodes with an Entity label, which there are none.
How can I get my repository to return Garments with designers of Person and Companys. I expect this is going to be as simple as an annotation, in order to fix.
(Note I've tried adding a #Node on the entity type with Person and Company as labels, however it just results in every node being added as both a Person and a Company).
JAVA SPRING :I am exploring JPA and am not sure of optimized way to design db and using save() of repository to save entity data right away into DB. Specifically, I have a basic class viz. Movie -
package com.kurshit.moviesmgr.vo;
import java.util.List;
public class Movie {
long movieId;
String title;
String yearOfRelease;
List<String> genere;
public Movie(long movieId, String title, String yearOfRelease, List<String> genere) {
super();
this.movieId = movieId;
this.title = title;
this.yearOfRelease = yearOfRelease;
this.genere = genere;
}
public Movie() {
// TODO Auto-generated constructor stub
}
public long getMovieId() {
return movieId;
}
public void setMovieId(long movieId) {
this.movieId = movieId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getYearOfRelease() {
return yearOfRelease;
}
public void setYearOfRelease(String yearOfRelease) {
this.yearOfRelease = yearOfRelease;
}
public List<String> getGenere() {
return genere;
}
public void setGenere(List<String> genere) {
this.genere = genere;
}
}
Each movie has list of Genere - List- it falls under, like - Action, Comedy, etc.
I am trying to create an interface that extends JpaRepository and use the inbuilt save method to save the Movie Data into DB.
I am not sure about how I should design my DB - As in, Questions like -
1. Shall I create two different tables for Movie and Genere wherein Movie table references to Genere ?
2. Shall I create just onw table and store all Genere's list as a single CSV in one column ?
3. Can I use repository's save() right away to save and map this data into respective tables.
Would really appreciate if someone can share any sources or sample code to refer or can offer any help.
Thanks much!
First of all, you should search look up #Entity annotation so that you can tell your ORM to create the necesary table for that entity.
Secondly, you need to ask yourself, how this application will work. It would be best in my opinion to create a genre entity as well, linked to Movie through a #ManyToMany relationship.
Try looking over the simple entity example here
https://spring.io/guides/gs/accessing-data-jpa/
First variant - with 'genre' as enum (if your genre is a fixed list):
#Data // it's Lombok annotation: https://projectlombok.org/features/Data
#NoArgsConstructor
#Entity
public class Movie implements Serializable {
#Id #GeneratedValue
private Integer id;
private String title;
private Integer yearOfRelease;
#ElementCollection
#Enumerated(EnumType.STRING)
#Column(name = "genre")
private List<Genre> genres;
public Movie(String title, Integer yearOfRelease, List<Genre> genres) {
this.title = title;
this.yearOfRelease = yearOfRelease;
this.genres = genres;
}
}
public enum Genre {
ACTION, COMEDY, ...;
}
public interface MovieRepo extends JpaRepository<Movie, Integer> {
}
In this case you create your movie like this:
Movie movie = new Movie("Title", 2000, Arrays.asList(ACTION, COMEDY));
movieRepo.save(movie);
Second variant - 'genre' as independent entity:
#Data
#NoArgsConstructor
#Entity
public class Movie implements Serializable {
// the same as previous one...
#ManyToMany
private List<Genre> genres;
}
#Data
#NoArgsConstructor
#Entity
public class Genre implements Serializable {
#Id private String name;
public Genre(String name) {
this.name = name
}
}
public interface MovieRepo extends JpaRepository<Movie, Integer> {
}
public interface GenreRepo extends JpaRepository<Genre, String> {
}
In this case you first create genres:
List<Genre> genres = genreRepo.saveAll(Arrays.asList(
new Genre("Action"),
new Genre("Comedy"),
));
Then create movie:
Movie movie = new Movie("Title", 2000, genres);
movieRepo.save(movie);
More info to read: Hibernate ORM User Guide - Collections
I was trying to learn Neo4j-OGM(Version:3.1.0).But I stucked with this Exception.Even tried with older versions but no use.And googled for help but couldn't find anything.Neo4j is the only choice for my project.I don't find anything wrong with my code. Can anyone help me with this? Thanks :) Here is my code..Sorry for this lengthy question.
#NodeEntity(label="Film")
public class Movie {
#GraphId
Long id;
#Property(name="title")
private String name;
public Movie(String name){
this.name=name;
}
public Movie(){}
}
#NodeEntity
public class Actor {
#Id
#GeneratedValue
private Long id;
#Property(name="name")
private String fullName;
#Relationship(type="ACTED_IN", direction=Relationship.OUTGOING)
private List<Role> filmography;
public Actor(String name){
this.fullName=name;
this.filmography=new ArrayList<>();
}
public Actor(){}
public void addRole(Role r){
this.filmography.add(r);
}
}
#RelationshipEntity(type="ACTED_IN")
public class Role {
#Id #GeneratedValue private Long relationshipId;
#Property private String title;
#StartNode private Actor actor;
#EndNode private Movie movie;
public Role(){}
public Role(String title,Actor actor,Movie movie){
this.actor=actor;
this.title=title;
this.movie=movie;
}
}
public class Main{
public static void main(String[] a){
Movie m1=new Movie("M1");
Actor a1=new Actor("A1");
Actor a2=new Actor("A2");
Movie m2=new Movie("M2");
Role r1=new Role("R1",a1,m1);
Role r2=new Role("R2",a2,m1);
Role r3=new Role("R3",a2,m2);
a1.addRole(r1);
a2.addRole(r2);
a2.addRole(r3);
Configuration configuration = new Configuration.Builder()
.uri("bolt://localhost")
.credentials("neo4j", "admin")
.build();
SessionFactory sessionFactory = new SessionFactory(configuration, "com.my.domain");
Session session=sessionFactory.openSession();
session.beginTransaction();
session.save(a1);
session.save(a2);
}
}
Check your package scanning in SessionFactory eg. new SessionFactory(configuration, "com.my.domain");
If declared package is not your entity package then this error also occurs
Try to check if bean for the class actor is loaded in the spring context properly. If it is not there in the context at run time due to wrong configuration (for example: #EntityScan is not defined with proper path), this exception can occur.
I am creating an application that uses a many to many relationship between employees and shifts. However, I am having difficulties in understanding how I can assign/connect an employee to a shift.
#Data
#Entity
public class Employee {
private #Id #GeneratedValue long employeeID;
private String Name;
#OneToMany(cascade = CascadeType.ALL)
private Set<Shift> shifts;
private Employee() {
}
public Employee(long employeeID, String Name) {
this.employeeID = employeeID;
this.Name = Name;
}
public Employee(long employeeID, String Name, Set<Shift> shifts) {
this.employeeID = employeeID;
this.Name = Name;
this.shifts = shifts;
}
public void setShift(Set<Shift> shifts) {
this.shifts = (Set<Shift>) shifts;
}
}
#Data
#Entity
public class Shift {
private #Id #GeneratedValue long Id;
private String shifts;
private Set<Employee> employee;
private Shift() {
}
public Shift(String shifts) {
this.shifts = shifts;
}
public Shift(String shiftPeriod,Set<Employee> employee ) {
this.shifts = shifts;
this.employee=employee;
}
public void setEmployee(Set<Employee> employee) {
this.employee = employee;
}
}
#Component
public class DatabaseLoader implements CommandLineRunner {
private final EmployeeRepository repository;
#Autowired
public DatabaseLoader(EmployeeRepository repository) {
this.repository = repository;
}
#Override
public void run(String... strings) throws Exception {
Shift shift = new Shift("Friday Morning");
Employee employee = new Employee(0001, "Adam Smith");
employee.setShift(shift);
this.repository.save(employee);
}
}
public interface ShiftRepository extends CrudRepository<Shift, Long>
public interface EmployeeRepository extends CrudRepository<Employee, Long>
Entities added into employees and shifts are saved but is there a way I can assign a shift to an employee in the DatabaseLoader class, as I've been stuck on finding a solution to this.
I know that I haven't included a method which attempts to connect employee and shifts but I don't how to approach this problem.
Thanks in advance
**EDIT: The new problem I have now is that I get the following message when trying to deploy in spring:
Unable to build Hibernate SessionFactory: Could not determine type for: java.util.Set, at table: shift, for columns: [org.hibernate.mapping.Column(employee)]
In my opinion:
A shift is an independent entity. It does not depend on any employees.
So Shift must not have a reference to Employee.
On the other hand an employee depend on several shifts, so Employee must have unidirectional #OneToMany association with Shift.
#OneToMany
private List<Shift> shifts;
And don't use cascading here because Employee and Shift are independent entities.
UPDATE
To "add a shift to an employee" we can use for example an Employee setter:
#Entity
public class Employee {
//...
private String name;
//...
#OneToMany
private List<Shift> shifts;
//...
public Employee(String name) {
this.name = name;
}
//...
public setShifts(Shift... shifts) {
this.shifts = Arrays.asList(shifts);
}
//...
}
then
Shift monday = shiftRepository.save(new Shift("Monday"));
Shift friday = shiftRepository.save(new Shift("Friday"));
Employee adamSmith = new Employee("Adam Smith");
adamSmith.setShifts(monday, friday);
employeeRepository.save(adamSmith);
It seems that you are using Hibernate under the hood, then all you have to do is to set the objects you want to save properly, like this:
#Component
public class DatabaseLoader implements CommandLineRunner {
private final EmployeeRepository repository;
private final ShiftRepository shiftRepository;
#Autowired
public DatabaseLoader(EmployeeRepository repository, ShiftRepository shiftRepository) {
this.repository = repository;
this.shiftRepository=shiftRepository;
}
#Override
public void run(String... strings) throws Exception {
Shift shift = new Shift("Friday Morning");
Employee employee = new Employee(0001, "Adam Smith");
employee.setShift(shift)
this.repository.save(employee);
}
The only difference is that I attached between the two objects and only then I try to save them. It's worth to mention that it's important to use Cascade.All or Cascade.persiste in order that hibernate will do the inserts on both entities.
I am new in hibernate, and I am experiencing the following problem. "Unique index or primary key violation". The problem appears due to the wrong mapping, but I spend hours to figure out why it is happening.
I have one super class called DataStructure
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class DataStructure {
private int DS_ID;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
public int getDataStructureID() {
return DS_ID;
}
Then the class Association which associate two elements. Some parts of the class are omitted here, just to simplify it.
#Entity
public class AssociationTemporal extends DataStructure {
private DataStructure elementA;
private DataStructure elementB;
#OneToOne
public DataStructure getElementA() {
return elementA;
}
public void setElementA(DataStructure elementA) {
this.elementA = elementA;
}
#OneToOne
public DataStructure getElementB() {
return elementB;
}
public void setElementB(DataStructure elementB) {
this.elementB = elementB;
}
}
This class serves as middle class between two classes of DataStructure type. Like this.
TP-Association-TP
TP class:
#Entity
public class TP extends DataStructure {
List<AssociationTemporal> listOfAssociatedTPs = new ArrayList<AssociationTemporal>();
#OneToMany
public List<AssociationTemporal> getListOfAssociatedTPs() {
return listOfAssociatedTPs;
}
public void setListOfAssociatedTPs(List<AssociationTemporal> listOfAssociatedTPs) {
this.listOfAssociatedTPs = listOfAssociatedTPs;
}
}
Or activites class
#Entity
public class Activities extends DataStructure {
String name;
List<AssociationTemporal> listOfAsso = new ArrayList<AssociationTemporal>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany
public List<AssociationTemporal> getListOfAsso() {
return listOfAsso;
}
public void setListOfAsso(List<AssociationTemporal> listOfAsso) {
this.listOfAsso = listOfAsso;
}
}
In the main I have added the following:
AssociationTemporal at = new AssociationTemporal();
TP tp1 = new TP();
TP tp2 = new TP();
at.setElementA(tp1);
at.setElementB(tp2);
session.save(tp1);
session.save(tp2);
session.save(at);
tp1.getListOfAssociatedTPs().add(at);
tp2.getListOfAssociatedTPs().add(at);
session.getTransaction().commit();
The problem occurs as soon as I try to add the same object of
tp1.getListOfAssociatedTPs().add(at);
tp2.getListOfAssociatedTPs().add(at);
Caused by: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "UK_12JEPI3MP039NKMGO47YW1HBI_INDEX_A ON PUBLIC.TP_ASSOCIATIONTEMPORAL(LISTOFASSOCIATEDTPS_DATASTRUCTUREID) VALUES (32770, 1)"; SQL statement:
insert into PUBLIC.TP_AssociationTemporal (TP_dataStructureID, listOfAssociatedTPs_dataStructureID) values (?, ?) [23505-183]
By the same mean the association can be made with Activities, etc...
Just use GenerationType.SEQUENCE and it will fix the problem.