I have 3 entities user, patient and doctor. Every doctor or patient is a user. I don't use inheritance strategy because I couldn't get an answer from my question here:
https://forum.hibernate.org/viewtopic.php?f=1&t=1012327Now I'm trying to achieve a similar point without inheritance. Schema is like that:
User <--- OneToOne --> Doctor
User <--- OneToOne --> Patient
Patient <--- ManyToMany --> Doctor
My entities:
User.java:
Code:
@Entity
@Table(name = "user")
public class User implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private int userId;
@Column(nullable = false, length = 25)
private String password;
@Column(nullable = false, columnDefinition = "int(11) unsigned")
private int type;
@Column(nullable = false, unique = true, length = 25)
private String username;
// bi-directional one-to-one association to Doctor
@OneToOne(mappedBy = "user", cascade = { CascadeType.ALL })
private Doctor doctor;
// bi-directional one-to-one association to Patient
@OneToOne(mappedBy = "user", cascade = { CascadeType.ALL })
private Patient patient;
public User()
{
}
public User(String username, String password)
{
this.password = password;
this.username = username;
}
//getters setters
//...
Patient.java:
Code:
@Entity
@Table(name = "patient")
public class Patient implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "foreign")
@GenericGenerator(name = "foreign", strategy = "foreign", parameters = { @Parameter(name = "property", value = "user") })
private int patientId;
private String address;
@Temporal(TemporalType.DATE)
private Date birthDate;
@Column(nullable = false)
private String name;
private String phone;
@Column(nullable = false)
private int sex;
@Column(nullable = false)
private String surname;
// bi-directional many-to-many association to Doctor
@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE })
@JoinTable(name = "doctorpatient", joinColumns = { @JoinColumn(name = "patientId") }, inverseJoinColumns = { @JoinColumn(name = "doctorId") })
private List<Doctor> doctors;
// bi-directional one-to-one association to User
@OneToOne(optional = false, cascade = { CascadeType.ALL })
@JoinColumn(name = "patientId", nullable = false)
private User user;
// bi-directional many-to-one association to Record
@OneToMany(mappedBy = "patient")
private List<Record> records;
public Patient()
{
}
public Patient(String name, String surname, int sex, User user)
{
this.name = name;
this.surname = surname;
this.sex = sex;
user.setType(User.PATIENT);
this.user = user;
this.user.setPatient(this);
}
//I've tried to obtain bi-directionality
public void addDoctor(Doctor doctor)
{
if (doctors == null)
doctors = new ArrayList<Doctor>();
doctors.add(doctor);
if (doctor.getPatients() == null)
doctor.setPatients(new ArrayList<Patient>());
doctor.getPatients().add(this);
}
//getters setters
//...
Doctor.java:
Code:
@Entity
@Table(name = "doctor")
public class Doctor implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "foreign")
@GenericGenerator(name = "foreign", strategy = "foreign", parameters = { @Parameter(name = "property", value = "user") })
private Integer doctorId;
private String address;
@Temporal(TemporalType.DATE)
private Date birthDate;
@Column(nullable = false)
private String name;
private String phone;
@Column(nullable = false, unique = true)
private String regNo;
@Column(nullable = false)
private int sex;
@Column(nullable = false)
private String surname;
// bi-directional one-to-one association to User
@OneToOne(optional = false, cascade = { CascadeType.ALL })
@JoinColumn(name = "doctorId", nullable = false)
private User user;
// bi-directional many-to-many association to Patient
@ManyToMany(mappedBy = "doctors", cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE })
private List<Patient> patients;
public Doctor()
{
}
public Doctor(String name, String surname, String regNo, int sex, User user)
{
this.name = name;
this.regNo = regNo;
this.sex = sex;
this.surname = surname;
user.setType(User.DOCTOR);
this.user = user;
this.user.setDoctor(this);
}
//I've tried to obtain bi-directionality
public void addPatient(Patient patient)
{
if (patients == null)
patients = new ArrayList<Patient>();
patients.add(patient);
if (patient.getDoctors() == null)
patient.setDoctors(new ArrayList<Doctor>());
patient.getDoctors().add(this);
}
//getters setters
//...
I've written junit tests for these entities. The doctor methods are working but patients' not.
This one creates 3 users(2 for patients 1 for doctor), 2 patients and a doctor.
DoctorTest.java:
Code:
@Test
public void testCreateDoctor()
{
for (int i = 0; i < unitNumber; i++)
{
boolean result = false;
User patientUser1 = new User("username" + (i + unitNumber), "password" + (i + unitNumber));
Patient patient1 = new Patient("name" + i, "surname" + i, i % 2, patientUser1);
User patientUser2 = new User("username" + (i + 2 * unitNumber), "password" + (i + 2 * unitNumber));
Patient patient2 = new Patient("name" + i, "surname" + i, i % 2, patientUser2);
User doctorUser = new User("username" + i, "password" + i);
Doctor doctor = new Doctor("name" + i, "surname" + i, "regNo" + i, i % 2, doctorUser);
doctor.addPatient(patient2);
doctor.addPatient(patient1);
result = doctorService.createDoctor(doctor);
assertTrue(result);
}
}
But that one doesn't work as I expect:
PatientTest.java:
Code:
@BeforeClass
public static void setUpBeforeClass() throws Exception
{
EntityManagerFactory emf = PersistenceManager.getInstance().getEntityManagerFactory();
patientService = new PatientServiceImpl(emf);
unitNumber = 20;
users = new User[4 * unitNumber];
doctors = new Doctor[unitNumber];
for (int i = 3 * unitNumber; i < 4 * unitNumber; i++)
{
User doctorUser = new User("username" + i, "password" + i);
Doctor doctor = new Doctor("name" + i, "surname" + i, "regNo" + i, i % 2, doctorUser);
doctors[i - 3 * unitNumber] = doctor;
}
}
@Test
public void testCreatePatient()
{
for (int i = 0; i < 3 * unitNumber; i++)
{
boolean result = false;
User patientUser = new User("username" + i, "password" + i);
Patient patient = new Patient("name" + i, "surname" + i, i % 2, patientUser);
patient.addDoctor(doctors[i % unitNumber]);
result = patientService.createPatient(patient);
assertTrue(result);
}
}
The for loop inside testCreatePatient executes up to unitNumber. When i becomes unitNumber + 1 (I mean 21), I'm getting
detached entity passed to persist: tr.com.stigma.db.entity.User. It creates 20 doctors, 20 patients and 40 users until i gets to 20. At the 21st insert, I try to add a new patient to an existing doctor, consequently to an existing user. How can I do this?
Stack trace is here:
Code:
javax.persistence.RollbackException: Error while committing the transaction
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:93)
at tr.com.stigma.db.service.PatientServiceImpl.createPatient(PatientServiceImpl.java:37)
at tr.com.stigma.db.service.test.PatientServiceTest.testCreatePatient(PatientServiceTest.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: tr.com.stigma.db.entity.User
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1215)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1148)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:81)
... 26 more
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: tr.com.stigma.db.entity.User
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127)
at org.hibernate.impl.SessionImpl.firePersistOnFlush(SessionImpl.java:834)
at org.hibernate.impl.SessionImpl.persistOnFlush(SessionImpl.java:826)
at org.hibernate.engine.CascadingAction$9.cascade(CascadingAction.java:357)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
at org.hibernate.engine.Cascade.cascade(Cascade.java:161)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:450)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:282)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:143)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:69)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:179)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
at org.hibernate.impl.SessionImpl.firePersistOnFlush(SessionImpl.java:834)
at org.hibernate.impl.SessionImpl.persistOnFlush(SessionImpl.java:826)
at org.hibernate.engine.CascadingAction$9.cascade(CascadingAction.java:357)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:425)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:362)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:338)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
at org.hibernate.engine.Cascade.cascade(Cascade.java:161)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:475)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsPersistent(DefaultPersistEventListener.java:159)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)
at org.hibernate.impl.SessionImpl.firePersistOnFlush(SessionImpl.java:834)
at org.hibernate.impl.SessionImpl.persistOnFlush(SessionImpl.java:826)
at org.hibernate.engine.CascadingAction$9.cascade(CascadingAction.java:357)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
at org.hibernate.engine.Cascade.cascade(Cascade.java:161)
at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:154)
at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:145)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:88)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:76)
... 26 more