I have a problem with hibernate running on JRun. It seems when there are multiple users using the system at the same time then I get these exceptions thrown. I get a JDBC.BatchUpdate exception on RegistrationAction:select() on line 508 which is 'List coll = session.createQuery("from is.op.actionform.RegistrationForm as temp where temp.registrant.username = '"+registrationBean.getUsername()+"'").list();'. This is especially suprising because I dont do a write to the database within this session.open()/close(). But I know hibernate does batch Updates so it could be from a previous session.open()/close(), in LoginAction, where I do persist a User64 to the database. It seems hibernate tries to insert the User64 object twice into the database and therefore I get Constraint violations because I have a 'unique constraint for email'. Iam afraid that the hibernate session is not thread safe and somehow two different sessions are trying to persist the same object. I was under the impression that JRUN with its JTA manager and JNDI pooling should work in conjunction with hibernate. However while reading the 'Hibernate in Action' book, I saw there was no support for the application server JRUN. Is there a recent update which does provide support or do I need to do something special for JRun.
While reading further about sessions, I discovered the type of transactions I do are 'session-per-request-with-detached-objects'. Also I test the app on the tomcat 5 and I cant reproduce the problem. Also because Jrun is a production server, it is hard for me to do debugging on it and logging verbosely. (hope this helps. If you need any further information, i will definitely provide it. And Thank you all for taking your time to help.)
Hibernate version: 2.1
Code:
Mapping documents:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping
package="is.op">
<class name="isc.db.User64" table="Users">
<id name="id" type="long" >
<column name="id" sql-type="number(20)" not-null="true"/>
<generator class="native"/>
</id>
<property name="firstName">
<column name="first_name" sql-type="varchar2(50)" not-null="true"/>
</property>
<property name="middleName">
<column name="middle_name" sql-type="varchar2(50)"/>
</property>
<property name="lastName">
<column name="last_name" sql-type="varchar2(50)" not-null="true"/>
</property>
<property name="email">
<column name="email" sql-type="varchar2(50)" not-null="true"/>
</property>
<property name="phone">
<column name="phone" sql-type="varchar2(50)"/>
</property>
<property name="fax">
<column name="fax" sql-type="varchar2(50)"/>
</property>
<property name="username">
<column name="username" sql-type="varchar2(30)" not-null="true"/>
</property>
<property name="company">
<column name="company" sql-type="varchar2(50)"/>
</property>
<property name="department">
<column name="department" sql-type="varchar2(50)"/>
</property>
<property name="title">
<column name="job_title" sql-type="varchar2(50)"/>
</property>
<property name="adminStatus">
<column name="admin_status" sql-type="char(1)"/>
</property>
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping
package="is.op.actionform">
<class name="is.op.actionform.RegistrationForm" table="Registrations">
<id name="registrantId" type="long">
<column name="registrant_id" sql-type="number(20)" not-null="true"/>
<generator class="foreign" >
<param name="property" >registrant</param><!-- ignored -->
</generator>
</id>
<one-to-one name="registrant" class="isc.db.User64" constrained="true"/>
<!--<property name="registeredById">
<column name="registered_by_id" sql-type="number(20)" />
</property> -->
<property name="title">
<column name="job_title" sql-type="varchar2(50)"/>
</property>
<property name="yearsAtLockheed">
<column name="years_at_lockheed" sql-type="number(4)"/>
</property>
<property name="registrationYear">
<column name="registration_year" sql-type="number(4)"/>
</property>
<property name="lmPeopleId">
<column name="lmpeople_id" sql-type="varchar2(20)"/>
</property>
<!-- <set name="guests" inverse="true" cascade="all-delete-orphan">
<key column="guest_id"/>
<one-to-many class="is.op.Guest"/>
</set> -->
<many-to-one name="lob" class="is.op.LineBusiness" column="lob_id" />
<many-to-one name="loc" class="is.op.Location" column="location_id" />
<many-to-one name="bus" class="is.op.Bus" column="bus_id" />
<!-- <many-to-one name ="registrant" class="isc.util.User64" column="registrant_id" /> -->
<many-to-one name="registeredBy" class="isc.db.User64" column="registered_by_id" />
</class>
</hibernate-mapping>
Code:
Code between sessionFactory.openSession() and session.close():
Full stack trace of any exception that occurs:
public ActionForward select(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response
)
{
Session session = null;
Transaction tr = null;
ActionForward forward = mapping.findForward(Constants.DATA_FORM);
try {
GregorianCalendar calendar = new GregorianCalendar();
//retreive op deadline from database. If not found within the database a default date of jan 1 1970 will be used.
Op op = OpService.getInstances().getBaseBean((long)calendar.get(Calendar.YEAR));
if(op == null){
op = new Op();
}
//check to see if deadline for registration has passed
if (!calendar.getTime().before(op.getDeadline())){
//TODO: Tell them why they are at a failure page.
//the deadline has passed
forward = mapping.findForward(Constants.FAILURE);
}else{
session = ApplicationBean.getSession();
tr = session.beginTransaction();
RegistrationForm registrationBean = (RegistrationForm)form;
//TODO: Remove all commented code about registeredby if not required
User64 loginUser = (User64)request.getSession().getAttribute(Constants.LOGIN_USER);
User64 registrant = null;
String registrant_username = registrationBean.getUsername();
if(registrant_username.equals("") || loginUser.getUsername().equals(registrant_username)){
//registering self
registrant = loginUser;
registrationBean=(RegistrationForm)BaseObjectService.getInstance().getBaseBean(RegistrationForm.class, registrant.getId());
if(registrationBean == null){
registrationBean = new RegistrationForm();
}else{
registrationBean.setAlreadyRegistered(true);
}
}else{
//registering someone else
registrant = new is.db.User64(registrant_username, session);
//check if the person is already registered and if the LoginUser is an admin
//if admin they can register/update anyone
List coll = session.createQuery("from is.op.actionform.RegistrationForm as temp where temp.registrant.username = '"+registrationBean.getUsername()+"'").list();
if (!coll.isEmpty()){
//get first one because only one can be present since username must be unique within table
RegistrationForm regForm = (RegistrationForm)coll.get(0);
//cannot register person if the logged in user is not a admin or not the registrar of the person
if(loginUser.getAdminStatus()==false && !(regForm.getRegisteredBy().getId()==loginUser.getId())){
if(regForm.getRegisteredById() != loginUser.getId()){
ActionErrors errors = new ActionErrors();
errors.add("registrant.registered", new ActionError("messages.general","The person has already been registered by someone else."));
saveErrors(request, errors);
tr.rollback();
session.close();
return mapping.findForward(Constants.MANAGER_PAGE_JSP);
}
}
//else let the admin/registrar update the user
BeanUtils.copyProperties(registrationBean, regForm);
registrationBean.setAlreadyRegistered(true);
forward = mapping.findForward(Constants.DATA_FORM);
}else{//new Registrant
registrationBean= new RegistrationForm();
forward = mapping.findForward(Constants.DATA_FORM);
}
}
registrationBean.setGuests(BaseObjectService.getInstance().getBaseBeanListWithFK(Guest.class,"host.registrantId", registrant.getId()));
registrationBean.setRegistrant(registrant);
registrationBean.setRegisteredBy(loginUser);
//set boolean to true if guests is not empty
registrationBean.setBringCustomers(!registrationBean.getGuests().isEmpty());
//populate Bus Drop Down Menus
populateDropdowns(registrationBean);
BeanUtils.copyProperties((RegistrationForm)form,registrationBean);
}
session.flush();
tr.commit();
}catch (Exception e){
servlet.log("RegistrationAction:select().", e);
e.printStackTrace();
ActionErrors errors = new ActionErrors();
errors.add("exception", new ActionError("messages.general", ""+this.getClass()+".select(): "+e));
this.saveErrors(request, errors);
try {
tr.rollback();
} catch (HibernateException e1) {
servlet.log("RegistrationAction:select().", e);
e1.printStackTrace();
}
forward = mapping.findForward(Constants.ERROR);
}finally{
try{
if(session!=null){
session.close();
}
}catch(Exception e){
servlet.log("RegistrationAction:select().", e);
e.printStackTrace();
ActionErrors errors = new ActionErrors();
errors.add("exception", new ActionError("messages.general", ""+this.getClass()+".select(): "+e));
this.saveErrors(request, errors);
forward = mapping.findForward(Constants.ERROR);
}
}
return forward;
}
Name and version of the database you are using:Oracle 9
The generated SQL (show_sql=true): The sql generated were straight forward and I dont think that is the issue but if Requested I will post it
Debug level Hibernate log excerpt: N/A