Hi everyone!
I'm rookie with Hibernate and I need some help with an issue.
I'm seeing
Hibernate(4.1.0.Final) is deleting and then re-inserting all rows on a many-to-many relation when I'm just trying to execute a "select service"
Here I explain what I'm doing:
In my system, any object that can be saved on the data base, extends from BaseDomain. Please check I'm re-defining equals() and hashCode() methods!! (I read this can be an issue)
Code:
public abstract class BaseDomain implements IPersistentObject {
protected String oid = OidGenerator.createId(); // oid: this just returns a UUID string.. is the ID for any object and any row in the table.
protected Integer version; // version: HIBERNATE unsaved-value="null" AND optimistic-lock="version"
private boolean removed = false;
@Override
public void setOid(String anOid) {
this.oid = anOid;
}
@Override
public String getOid() {
return oid;
}
@Override
public void setVersion(Integer aVersion) {
this.version = aVersion;
}
@Override
public Integer getVersion() {
return version;
}
public void setRemoved(boolean removed) {
this.removed = removed;
}
public boolean isRemoved() {
return removed;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || !(this.isAPersistenObject(obj))) {
return false;
}
IPersistentObject other = (IPersistentObject) obj;
if (oid == null)
return false;
return oid.equals(other.getOid());
}
private boolean isAPersistenObject(Object obj) {
return obj instanceof IPersistentObject;
}
public boolean equalsToDTO(AbstractDTO aBaseDTO) {
return oid.equals(aBaseDTO.getOid());
}
@Override
public int hashCode() {
if (oid != null) {
return oid.hashCode();
} else {
return super.hashCode();
}
}
@Override
public String toString() {
return this.getClass().getName() + " [id=" + oid + "]";
}
}
Then I have 2 object and a many to many relation. A Team can have many Users, here are the java classes and the hbml files:
////////////// USER /////////////////////
Code:
public class User extends BaseDomain{
protected String userName;
protected String password;
public User() {
}
public User(String anUserName, String aPassword) {
this.userName = anUserName;
this.password = aPassword;
}
public String getUserName() {
return userName;
}
public void setUserName(String anUserName) {
this.userName = anUserName;
}
public String getPassword() {
return password;
}
public void setPassword(String aPassword) {
this.password = aPassword;
}
}
Code:
<hibernate-mapping>
<class name="user.domain.User" table="USUARIO" optimistic-lock="version"> // I tried select-before-update="true"
<id name="oid" column="OID">
<generator class="assigned" />
</id>
<version column="version" name="version" unsaved-value="null"/>
<property name="userName" column="user_name" unique="true" />
<property name="password" column="password" />
<property name="removed" column="removed" />
</class>
<!-- Queries for User -->
<query name="getUserByUserName">
<![CDATA[from User user where user.userName = :anUserName]]>
</query>
<query name="getUsersByOids">
<![CDATA[from User user where user.removed = :isRemoved and user.oid IN (:oids)]]>
</query>
</hibernate-mapping>
////////////// TEAM /////////////////////
Code:
public class Team extends BaseDomain {
private String name;
private final Collection<User> users = new HashSet<User>();
public Team() {
}
public Team(String aName, Collection<User> users) {
this.name = aName;
this.users.addAll(users);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<User> getUsers() {
return users;
}
protected void setUsers(Collection<User> users) {
this.users.addAll(users);
}
public void addUser(User anUser) {
this.users.add(anUser);
}
}
Code:
<hibernate-mapping>
<class name="user.domain.team.Team" table="TEAM" optimistic-lock="version"> // I tried select-before-update="true"
<id name="oid" column="OID">
<generator class="assigned" />
</id>
<version column="version" name="version" unsaved-value="null"/>
<property name="name" column="name" unique="true" />
<bag name="users" table="TEAM_USER"> // <--- THIS IS THE MANY TO MANY RELATION!
<key column="oid_team" />
<many-to-many column="oid_user" class="user.domain.User" />
</bag>
</class>
<!-- Queries for User -->
<query name="getTeamByName">
<![CDATA[from Team team where team.name = :aName]]>
</query>
</hibernate-mapping>
Then, I have the "Team service" (summarized).. And the problem...
When I try to ejecute the getTeam() method on TeamServiceImpl (see the code below), hibernate is removing and re-inserting all TEAM_USERS relation. I can add the SQL statments if is necesary.
When Hibernate does this.. the versino field on TEAM is changed.
Because I'm using Data transfer objects, I need to use a method like "checkDTOConcurrency" on any service that change any property on TEAM.
So when I try to get any TEAM and then execute any method that check the TEAM version (like remove or update), it fails throwing DTOConcurrencyException.
Code:
public class TeamServiceImpl extends AbstractServiceImpl implements TeamServiceBI {
@Override
public TeamDTO getTeam(String sessionToken, TeamDTO aTeamDTO) throws UnknownTeamException {
// get the Team from a respository.
Team aTeam = this.getTeamRespository().getTeamByDTO(aTeamDTO);
// create a new Data transfer object with data from the real team.
return (TeamDTO) TeamDTOFactory.getInstance().getDTO(aTeam);
}
@Override
public void removeTeam(String sessionToken, TeamDTO aTeamDTOToRemove) throws DTOConcurrencyException,
UnknownTeamException {
ItemTracker theItemTracker = this.getItemTrackerRespository().getItemTracker();
Team aTeam = this.getTeamRespository().getTeamByDTO(aTeamDTOToRemove);
this.checkDTOConcurrency(aTeamDTOToRemove, aTeam); // CHECK THIS... WHERE IS A PART OF THE PROBLEM.
theItemTracker.removeTeam(aTeam);
}
// I add this method here so you can see it.
final protected void checkDTOConcurrency(AbstractDTO aDTO, BaseDomain aBaseDomianObject)
throws DTOConcurrencyException {
if (!aDTO.getVersion().equals(aBaseDomianObject.getVersion())) {
throw new DTOConcurrencyException("The entity that you what to modifier had been used by other user.");
}
}
}
See an example of my service decoration with Spring:
//////// SOME PART OF MY APP_CONTEXT FILE ///////////
Code:
<bean id="teamServiceTarget" class="user.service.team.TeamServiceImpl"
parent="abstractServiceImpl">
</bean>
<bean id="teamService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target" ref="teamServiceTarget" />
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
<property name="preInterceptors">
<list>
<bean class="base.security.ServiceSecurityPreInterceptor" /> // THIS DOES NOT MODIFIES AN OBJECT
</list>
</property>
</bean>
If is necesary to see all the code, you can check it on google code:
svn checkout http://mg-bdoo-tp-items.googlecode.com/svn/trunk/ mg-bdoo-tp-items-read-only
(You should start seeing TeamCreateServiceTest)
Thank you very much!