2

Last 4 hours I was dealing up with this JPA problem. At last I gave up so I am asking for a help from you. I tried almost every suggested solutions what I saw so far.

I tried,

1) Mapping changes (@ManyToOne, @OneToOne, @OneToMany)

2) Cascade options (PERSIST, MERGE, ALL..)

3) Disabling Cache

3) Many other tries that are not spesific like 1,2 and 3. Just for a hope. :)

testClass

public class testClass {
public static void main(String[] args) {

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("CSE_482_Project_4_-_PersistencePU");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();

    File parentFile = new File("cleardata");
    File[] allFiles = parentFile.listFiles();
    File myFile;
    int k = 1;

    for(int f =0; f<allFiles.length; f++){
    try {
        myFile=allFiles[f];
        BufferedReader br = new BufferedReader(new FileReader(myFile));

        String tempDate = br.readLine();
        tempDate = tempDate.substring(17);
        String[] tempDateArr = tempDate.split(" ");
        int day = Integer.parseInt(tempDateArr[0]);
        int month = Integer.parseInt(tempDateArr[1]);
        int year = Integer.parseInt(tempDateArr[2]);

        Date leavingDate = new Date(year, month, day);

        String studentNumber = br.readLine().substring(13);
        boolean minor = true;
        if (br.readLine().contains("false")) {
            minor = false;
        }
        Student stu = new Student(studentNumber, leavingDate);
        stu.setMinorDegree(minor);

        tx.begin();
        em.persist(stu);
        tx.commit();

        String currentLine;
        Slot s;
        Course c;
        SlotAndCourse sc;
        int semester = 0;
        String courseCode = "";
        String slotName = "";
        int credit = 0;
        String termTaken = "";
        int yearTaken = 0;
        String grade = "";
        boolean semesterSet = false;
        boolean courseSet = false;
        boolean gradesSet = false;

        int count = 0;


        while ((currentLine = br.readLine()) != null) {
            String[] arr = currentLine.split(" ");

            if (arr[0].equals("semester")) {
                semester = Integer.parseInt(arr[1]);
                semesterSet = true;
            } else if (arr[0].matches("^([0-9]+[a-zA-Z]+|[a-zA-Z]+[0-9]+)[0-9a-zA-Z]*$")) { // contains both latters and digits CS112
                courseCode = arr[0];
                slotName = arr[1];
                credit = Integer.parseInt(arr[2]);
                courseSet = true;
            } else if (arr[0].equals("numberofattempts")) {
                int n = Integer.parseInt(arr[1]);
                for (int i = 0; i < n; i++) {
                    currentLine = br.readLine();
                    System.out.println(currentLine);
                    arr = currentLine.split(" ");
                    yearTaken = Integer.parseInt(arr[0]);
                    termTaken = arr[1];
                    grade = arr[2];
                }
                gradesSet = true;
            }

            if (gradesSet && courseSet && semesterSet) {
                s = new Slot();
                c = new Course(courseCode);

                s.setCredit(credit);
                s.setSemester(semester);
                s.setSlotName(slotName);
                s.setSlotCode("" + k);

                c.setCourseCode(courseCode);

                sc = new SlotAndCourse(s,c,yearTaken,termTaken);
                sc.setCourse(c);
                sc.setSlot(s);
                sc.setGrade(grade);

                tx.begin();

                em.clear();    // just a try, but didn't work
                em.persist(sc);
                tx.commit();                 

                courseSet = false;
                semesterSet = false;
                gradesSet = false;
                k++;
            }

        }

    } catch (Exception ex) {
        System.out.println(ex.toString());
        ex.printStackTrace();
    }
    }
     em.close();
}

}

Student Class

@Entity
@Cacheable(false)
public class Student implements Serializable {

@Id
private String studentNumber;

@Temporal(TemporalType.DATE)
private Date leavIngDate;
private boolean mInorDegree;

public Student() {
}

public Student(String studentNumber, Date leavingDate) {
    this.studentNumber = studentNumber;
    this.leavIngDate = leavingDate;
}

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof Slot)) {
        return false;
    }
    Student other = (Student) obj;
    if ((this.studentNumber == null &&  other.studentNumber != null) || (this.studentNumber != null && !this.studentNumber.equals(other.studentNumber))) {
        return false;
    }
    return true;
}

@Override
public int hashCode() {
   int hash = 0;
    hash += (studentNumber != null ? studentNumber.hashCode() : 0);
    return hash;
}

  // setters and getters

Course Class

  @Entity
  @Cacheable(false)
  public class Course implements Serializable {

  @Id
  private String courseCode;

public Course() {
}

public Course(String courseCode) {
    this.courseCode = courseCode;
}

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof Course)) {
        return false;
    }
    Course other = (Course) obj;
    if ((this.courseCode == null && other.courseCode != null) || (this.courseCode != null && !this.courseCode.equals(other.courseCode))) {
        return false;
    }
    return true;
}

@Override
public int hashCode() {
    int hash = 0;
    hash += (courseCode != null ? courseCode.hashCode() : 0);
    return hash;
}

  //setters and getters

Slot Class

  @Entity
  @Cacheable(false)
  public class Slot implements Serializable {
  @Id
private String slotCode;
private String slotName;
private int credIt;
private int semester;

public Slot() {
}

public Slot(String slotCode, String slotName) {
    this.slotCode = slotCode;
    this.slotName = slotName;
}

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof Slot)) {
        return false;
    }
    Slot other = (Slot) obj;
    if ((this.slotCode == null && other.slotCode != null) || (this.slotCode != null && !this.slotCode.equals(other.slotCode))) {
        return false;
    }
    return true;
}

@Override
public int hashCode() {
    int hash = 0;
    hash += (slotCode != null ? slotCode.hashCode() : 0);
    return hash;

     //setters getters

SlotAndCourse Class

@Entity
@Cacheable(false)
public class SlotAndCourse implements Serializable {
@EmbeddedId
protected SlotAndCoursePK slotAndCoursePK;

@JoinColumn(name = "SLOTCODE", referencedColumnName = "SLOTCODE", insertable = false, updatable = false)
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.ALL})
private Slot slot;

@JoinColumn(name = "COURSECODE", referencedColumnName = "COURSECODE", insertable = false, updatable = false)
@ManyToOne(cascade = CascadeType.PERSIST)
private Course course;

private String grade;

public SlotAndCourse() {
}

public SlotAndCourse(SlotAndCoursePK slotAndCoursePK) {
    this.slotAndCoursePK = slotAndCoursePK;
}

public SlotAndCourse(String slotCode, String courseCode, int yearTaken, String termTaken) {
    this.slotAndCoursePK = new SlotAndCoursePK(slotCode, courseCode, yearTaken, termTaken);
}
public SlotAndCourse(Slot s, Course c, int yearTaken, String termTaken) {
    this.slot = s;
    this.course = c;
    this.slotAndCoursePK = new SlotAndCoursePK(s.getSlotCode(), c.getCourseCode(),yearTaken,termTaken);
}

@Override
public boolean equals(Object obj) {
   if(obj instanceof SlotAndCourse){
        SlotAndCourse arg = (SlotAndCourse)obj;
        return this.slotAndCoursePK.equals(arg.slotAndCoursePK);
    }
    else if(obj instanceof SlotAndCoursePK){
        SlotAndCoursePK arg = (SlotAndCoursePK)obj;
        return this.slotAndCoursePK.equals(arg);
    }
    return false;
}

@Override
public int hashCode() {
    int hash = 0;
    hash += (slotAndCoursePK != null ? slotAndCoursePK.hashCode() : 0);
    return hash;
}

SlontAndCoursePK Class

 @Embeddable
 public class SlotAndCoursePK implements Serializable{

protected String slotCode;
protected String courseCode;
protected int yearTaken;
protected String termTaken;

public SlotAndCoursePK() {
}

public SlotAndCoursePK(String slotCode, String courseCode, int yearTaken, String termTaken) {
    this.slotCode = slotCode;
    this.courseCode = courseCode;
    this.yearTaken = yearTaken;
    this.termTaken = termTaken;
}

 @Override
public int hashCode() {
    int hash = 0;
    hash += (slotCode != null ? slotCode.hashCode() : 0);
    hash += (courseCode != null ? courseCode.hashCode() : 0);
    return hash;
}

@Override
public boolean equals(Object object) {
    // TODO: Warning - this method won't work in the case the id fields are not set
    if (!(object instanceof SlotAndCoursePK)) {
        return false;
    }
    SlotAndCoursePK other = (SlotAndCoursePK) object;
    if ((this.slotCode == null && other.slotCode != null) || (this.slotCode != null && !this.slotCode.equals(other.slotCode))) {
        return false;
    }
    if ((this.courseCode == null && other.courseCode != null) || (this.courseCode != null && !this.courseCode.equals(other.courseCode))) {
        return false;
    }
    return true;
}

 // setters and getters

I feel so bad for coping and pasting all the codes here. I hope someone could help me about what I am missing. First part of testClass is reading from existing well-structured text files and fill related datafields.

What happens when I run a debug is; First everything goes fine, it adds students, courses, slotAndCourses to db as expected but when a slotAndCourse instance with an existing courseCode in db is created and tring to be persisted(not sure if it is the correct word) to database it gives me duplicate entry for primary key in course table.

Error looks like this:

javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'cse110' for key 'PRIMARY' Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'cse110' for key 'PRIMARY' Error Code: 1062 Error Code: 1062 Call: INSERT INTO COURSE (COURSECODE) VALUES (?) Call: INSERT INTO COURSE (COURSECODE) VALUES (?)

user2949556
  • 47
  • 1
  • 7
  • 1
    Deleted my answer now that I understand better what you are doing. It's not related to your question so I will make a comment, it would be easier to read your code if variables were declared where they were used instead of in higher scopes that have no other uses of the variables. In the form you have it, the reader is left with the job of reading everything for every variable to see if it is used elsewhere. More importantly, you may find your own problem this way. – Brian Topping May 03 '15 at 17:17
  • 1
    Is the database empty before you start? – gfelisberto May 03 '15 at 17:21
  • Persistence setting is set to "Drop and Create" so every time it first drops all the schemas and than re-creates. This is just a template I now that it is a bit messy. I will try to make it simpler to read as soon as possible :) – user2949556 May 03 '15 at 20:42
  • The point of Peter's solution below is that you need to look up the Course entity from the database if it exists and use that one. If you don't use the managed instance read from the database, JPA will assume it is new and so try to insert it again. Hence the code above trying to find the course and only creating a new one if it doesn't already exist. What part are you having problems with? – Chris May 04 '15 at 23:23
  • Peter's solution helped me a lot. Before posting here I tried that but only checking the database if that course exists or not did't solve my problem. Below I have posted my last changes and I think that it work now! Thanks a lot for everyone. @peter – user2949556 May 05 '15 at 02:22

2 Answers2

0

My guess is that cleardata is a directory containing text files. Every file in the cleardata directory contains data for a single student.

I suppose a course can be taken by many students so you shouldn't create a course every time you find it in a text file.

Instead of c = new Course(courseCode); you should search the database to see if there is a saved version of the course. A similar thing might help (I didn't test this code):

public Course findOrCreateCourse(String courseCode) {
    Course course = em.find(Course.class, courseCode);
    if (course == null)
        course = new Course(courseCode);
    }
    return course;
}

You might have the same problem with other entities.

Peter Bagyinszki
  • 1,069
  • 1
  • 10
  • 29
  • Tried to check database if there exists such course with that courseNo or not. I was wondering that it would work but I couldn't made it work. I will try again because it makes sense. Your guesses are exactly right by the way. I will try this and share the result as soon as possible. Thanks. – user2949556 May 03 '15 at 20:48
  • Updated the code to be simpler - you can use `find` since `courseCode` is primary key. – Peter Bagyinszki May 03 '15 at 21:00
  • I tried what you said, it didn't work or I coudn't make it worked. There is a part that I don't understand. According to this solution, it says, if a course you want to include in SlotAndCourse object exists in database(at Course table), don't create that object. So this means that I can't add more than one row to my SlotAndCourse table with a courseCode='CSE101'. I tried to explain what I want to say with an image. Hope I can explain what I want to say. Thanks again for your consideration. Image Link: [link]http://oi59.tinypic.com/vo7b83.jpg – user2949556 May 04 '15 at 15:35
0

Today I finally found the solution for my problem. Below is the updated code for anyone who needs it. The main problem I found was that I wasn't persisting Course and Slot objects separately. I was thinking that persisting combined SlotAndCourse object deals also with inserting Course and Slot objects. My observation is that if three of them(Course,Slot,CourseAndSlot) are persisted together then things are working better. I think the relation is set correctly then. I could be wrong about some concepts but my code works correctly now!

Thanks for everyone.

I also tried to make my code easier to read:

testClass

public class testClass {

static EntityManagerFactory emf = Persistence.createEntityManagerFactory("CSE_482_Project_4_-_PersistencePU");
static EntityManager em = emf.createEntityManager();
static EntityTransaction tx = em.getTransaction();

public static void main(String[] args) {

    // File related variables
    File parentFile = new File("cleardata");
    File[] allFiles = parentFile.listFiles();
    File myFile;
    BufferedReader br;
    String currentLine;

    // Student object and its variables
    Student stu;
    Date leavingDate;
    String studentNumber;
    boolean minor;

    // Object declerations
    Slot s;
    Course c;
    SlotAndCourse sc;

    // datafields of objects
    int semester = 0;
    String courseCode = "";
    String slotName = "";
    int credit = 0;
    String termTaken = "";
    int yearTaken = 0;
    String grade = "";
    String slotCode = "";

    // to ensure if every related field is set or not
    boolean semesterSet = false;
    boolean courseSet = false;
    boolean gradesSet = false;

    for (int f = 0; f < allFiles.length; f++) {     // travesre through files in directory
        try {
            myFile = allFiles[f];
            br = new BufferedReader(new FileReader(myFile));

            String tempDate = br.readLine();
            tempDate = tempDate.substring(17);
            String[] tempDateArr = tempDate.split(" ");
            int day = Integer.parseInt(tempDateArr[0]);
            int month = Integer.parseInt(tempDateArr[1]);
            int year = Integer.parseInt(tempDateArr[2]);

            leavingDate = new Date(year, month, day);
            studentNumber = br.readLine().substring(13);

            minor = true;
            if (br.readLine().contains("false")) {
                minor = false;
            }
            stu = new Student(studentNumber, leavingDate);
            stu.setMinorDegree(minor);
            em.persist(stu);

            while ((currentLine = br.readLine()) != null) {
                String[] arr = currentLine.split(" ");

                if (arr[0].equals("semester")) {
                    semester = Integer.parseInt(arr[1]);
                    semesterSet = true;
                } else if (arr[0].matches("^([0-9]+[a-zA-Z]+|[a-zA-Z]+[0-9]+)[0-9a-zA-Z]*$")) { // contains both latters and digits CS112
                    slotCode = arr[0];
                    slotName = arr[1];
                    credit = Integer.parseInt(arr[2]);
                    courseSet = true;
                } else if (arr[0].equals("numberofattempts")) {
                    int n = Integer.parseInt(arr[1]);
                    for (int i = 0; i < n; i++) {
                        currentLine = br.readLine();
                        arr = currentLine.split(" ");
                        yearTaken = Integer.parseInt(arr[0]);
                        termTaken = arr[1];
                        grade = arr[2];
                        courseCode = arr[3];
                        gradesSet = true;

                        if (gradesSet && courseSet && semesterSet) {

                            tx.begin();

                            s = findOrCreateSlot(slotCode, slotName);
                            c = findOrCreateCourse(courseCode);

                            s.setCredit(credit);
                            s.setSemester(semester);
                            s.setSlotName(slotName);
                            s.setSlotCode(slotCode);

                            sc = findOrCreateSlotAndCourse(s, c, yearTaken, termTaken);
                            sc.setCourse(c);
                            sc.setSlot(s);
                            sc.setGrade(grade);

                            em.persist(s);
                            em.persist(c);
                            em.persist(sc);
                            tx.commit();

                            courseSet = false;
                            semesterSet = false;
                            gradesSet = false;
                        }
                    }
                }
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            ex.printStackTrace();
        }
    }
    em.close();
}

public static SlotAndCourse findOrCreateSlotAndCourse(Slot s, Course c, int yearTaken, String termTaken) {
    SlotAndCoursePK pk = new SlotAndCoursePK(s.getSlotCode(), c.getCourseCode(), yearTaken, termTaken);
    SlotAndCourse slotandcourse = em.find(SlotAndCourse.class, pk);
    if (slotandcourse == null) {
        slotandcourse = new SlotAndCourse(s, c, yearTaken, termTaken);
    }
    return slotandcourse;
}

public static Course findOrCreateCourse(String courseCode) {
    Course course = em.find(Course.class, courseCode);
    if (course == null) {
        course = new Course(courseCode);
    }
    return course;
}

public static Slot findOrCreateSlot(String slotCode, String slotName) {
    Slot slot = em.find(Slot.class, slotCode);
    if (slot == null) {
        slot = new Slot(slotCode, slotName);
    }

    return slot;
}

}

user2949556
  • 47
  • 1
  • 7
  • This just boils down to the problem code always creating a new course/slot and calling persist on it, while this code checks to see if the course/slot already exists and uses the existing instance if it does. If it exists, the persist call is a no-op, and there should be no reason to explicitly call em.persist on the slot/course instance rather then just calling em.persist(slotandcourse) and letting the persist call cascade. The problem code might have worked as well if you switched to using em.merge instead of persist, as JPA then works out if the instance exists or not for you. – Chris May 08 '15 at 14:30