Results 1 to 6 of 6

Thread: SDN Can't override final RelationshipBacked.hashCode(), equals(java.lang.Object)

  1. #1
    Join Date
    Feb 2012
    Posts
    9

    Default SDN Can't override final RelationshipBacked.hashCode(), equals(java.lang.Object)

    Hi,
    I am trying to implement some clone functionality for SDN Entities with advanced mapping (aspectj compile time weaving). In my test scenarios I am creating new nodes from existing ones. I need to override public int hashCode() and public boolean equals(Object obj) methods in entities. Unit test results are OK within Eclipse project with AJDT nature (in attachment), but running it by maven (mvn clean test) throws aspectj compiler exception :

    Code:
    INFO] --- aspectj-maven-plugin:1.4:compile (default) @ test ---
    ERROR] can't override final int org.springframework.data.neo4j.aspects.core.RelationshipBacked.hashCode()
    ERROR] can't override final boolean org.springframework.data.neo4j.aspects.core.RelationshipBacked.equals(java.lang.Objec
    t)
    When I try to move hashCode() and equals(Object obj) methods to abstract class level, then there is another exceptions:

    Code:
    [INFO] --- aspectj-maven-plugin:1.4:compile (default) @ test ---
    [ERROR] inter-type declaration from org.springframework.data.neo4j.aspects.support.relationship.Neo4jRelationshipBacking
    conflicts with existing member: boolean user.GraphEntity.equals(java.lang.Object)
    [ERROR] inter-type declaration from org.springframework.data.neo4j.aspects.support.relationship.Neo4jRelationshipBacking
    conflicts with existing member: int user.GraphEntity.hashCode()
    [ERROR] inter-type declaration from org.springframework.data.neo4j.aspects.support.node.Neo4jNodeBacking conflicts with e
    xisting member: boolean user.GraphEntity.equals(java.lang.Object)
    [ERROR] inter-type declaration from org.springframework.data.neo4j.aspects.support.node.Neo4jNodeBacking conflicts with e
    xisting member: int user.GraphEntity.hashCode()
    [ERROR] inter-type declaration from org.springframework.data.neo4j.aspects.support.relationship.Neo4jRelationshipBacking
    conflicts with existing member: int user.GraphEntity.hashCode()
    [ERROR] inter-type declaration from org.springframework.data.neo4j.aspects.support.node.Neo4jNodeBacking conflicts with e
    xisting member: int user.GraphEntity.hashCode()
    [ERROR] inter-type declaration from org.springframework.data.neo4j.aspects.support.relationship.Neo4jRelationshipBacking
    conflicts with existing member: boolean user.GraphEntity.equals(java.lang.Object)
    [ERROR] inter-type declaration from org.springframework.data.neo4j.aspects.support.node.Neo4jNodeBacking conflicts with e
    xisting member: boolean user.GraphEntity.equals(java.lang.Object)
    Please make that methods non final if you have not another reason.
    Or maybe there is anothor solution for this clone use case.

    Code:
    	@Test
    	public void cloneUser(){
    		User findedUser = userDao.findByPropertyValue("userName", "a");
    		assertThat(findedUser.getId(), notNullValue());
    		User clone = (User) findedUser.clone();
    		assertThat(findedUser, equalTo(clone));
    		assertThat(findedUser.getId(), equalTo(clone.getId()));
    		assertThat(findedUser.getUserName(), equalTo(clone.getUserName()));
    		clone.setUserName("x");
    		clone.setId(null);
    		assertThat(clone.getId(), nullValue());
    		User savedClone = userDao.save(clone);
    		assertThat(savedClone.getId(), notNullValue());
    		assertThat(savedClone.getUserName(), equalTo(clone.getUserName()));
    		assertThat(findedUser, not(equalTo(savedClone)));
    		assertThat(findedUser.getId(), not(equalTo(savedClone.getId())));
    	}
    Thanks
    Vlado K.
    Attached Files Attached Files

  2. #2
    Join Date
    May 2012
    Posts
    107

    Default

    Vlado,

    I can't tell you why those methods are final, or if they can be made non-final. We will have to look at it some more. In the mean time, could you show us what your User class (and any super/ subclasses) looks like please?

    In the short term, you will need a workaround. Perhaps a wrapper for User that implements equals and hashcode, and delegates calls to the User object?

    Regards,
    Lasse

  3. #3
    Join Date
    Jan 2011
    Location
    Dresden, Germany
    Posts
    525

    Default

    What is the use-case for overriding hashCode and equals? Or cloning the entities in the first place?

    Perhaps what you really want to do is to create a separate class that is use-case specific and contains only the data you need (which might be fetched even from deeper in the graph).

    After all the entities are live view of the database, mapped to the appropriate node and relationship.

    That's why the hashCode & equals check the node & rel-id for equality and nothing else. Overriding them might break a lot of behavior in your system and the infrastructure.

  4. #4
    Join Date
    Feb 2012
    Posts
    9

    Default

    Hi Guys,
    I have common create copy functionality for every entity in application. There are many jpa entities in which equals and hash methods are based on primaryKey (long or composite key). So create copy means creating clone instance, setting primary key to null and then save (entitymanager.persist()). New copied instance has new primaryKey with properties equal to original one. The same thing I need with SDN entities. I simulate it in this unit test.

    Code:
    	public void cloneUser(){
    		User findedUser = userDao.findByPropertyValue("userName", "a");
    		assertThat(findedUser.getId(), notNullValue());
    		User clone = (User) findedUser.clone();
    		assertThat(findedUser, not(sameInstance(clone)));
    		assertThat(findedUser, equalTo(clone));
    		assertThat(findedUser.getId(), equalTo(clone.getId()));
    		assertThat(findedUser.getUserName(), equalTo(clone.getUserName()));
    		clone.setUserName("x");
    		clone.setId(null);
    		assertThat(clone.getId(), nullValue());
    		User savedClone = userDao.save(clone);
    		assertThat(savedClone.getId(), notNullValue());
    		assertThat(savedClone.getUserName(), equalTo(clone.getUserName()));
    		assertThat(findedUser, not(sameInstance(savedClone)));
    		assertThat(findedUser, not(equalTo(savedClone)));
    		assertThat(findedUser.getId(), not(equalTo(savedClone.getId())));
    	}
    My first attempt was to implement only clone() method and setter for id() by calling setPersistentState(null);.

    Code:
    package user;
    
    import org.springframework.data.neo4j.annotation.Indexed;
    import org.springframework.data.neo4j.annotation.NodeEntity;
    
    @NodeEntity
    public class User implements Cloneable {
    	
    	@Indexed(unique=true)
    	String userName;
    	
    	public Long getId() {
    		return getNodeId();
    	}
    
    	public void setId(Long id) {
    		if (id == null){
    			setPersistentState(null);
    		}
    	}
    	
    	public String getUserName() {
    		return userName;
    	}
    
    	public void setUserName(String userName) {
    		this.userName = userName;
    	}
    
    	@Override
    	public User clone() {
    		try {
    			return (User) super.clone();
    		} catch (CloneNotSupportedException e) {
    			return null;
    		}
    	}
    
    }
    In this case there is error on this line :
    assertThat(findedUser, not(sameInstance(savedClone)));

    java.lang.AssertionError:
    Expected: not sameInstance(<user.User@5>)
    got: <user.User@5>

    Clone entity is still the same instance when i call dao.save().

    Next I was trying to rewrite clone method by creating empty new instance without reference to backing node and copy the properties from original. Because I think and Michael said that the hash and equals are based only on node id, i must define @GraphId id property.

    Code:
    package user;
    
    import org.springframework.data.neo4j.annotation.GraphId;
    import org.springframework.data.neo4j.annotation.Indexed;
    import org.springframework.data.neo4j.annotation.NodeEntity;
    
    @NodeEntity
    public class User implements Cloneable {
    	
    	@GraphId
    	Long id;
    
    	@Indexed(unique=true)
    	String userName;
    	
    	public Long getId() {
    		return id;
    	}
    
    	public void setId(Long id) {
    		this.id = id;
    	}
    	
    	public String getUserName() {
    		return userName;
    	}
    
    	public void setUserName(String userName) {
    		this.userName = userName;
    	}
    
    	@Override
    	public User clone() {
    		User clone = new User();
    		clone.setId(this.getId());
    		clone.setUserName(this.getUserName());
    		return clone;
    	}
    }
    In this case error was on line :
    assertThat(findedUser, equalTo(clone));

    java.lang.AssertionError:
    Expected: <user.User@6e3e5e>
    got: <user.User@1>

    That was the reason why I need to override equals and hash methods.
    This version of User passing through unit test.

    Code:
    package user;
    
    import org.apache.commons.lang.builder.EqualsBuilder;
    import org.apache.commons.lang.builder.HashCodeBuilder;
    import org.springframework.data.neo4j.annotation.GraphId;
    import org.springframework.data.neo4j.annotation.Indexed;
    import org.springframework.data.neo4j.annotation.NodeEntity;
    
    @NodeEntity
    public class User implements Cloneable {
    	
    	@GraphId
    	Long id;
    
    	@Indexed(unique=true)
    	String userName;
    	
    	public Long getId() {
    		return id;
    	}
    
    	public void setId(Long id) {
    		this.id = id;
    	}
    	
    	public String getUserName() {
    		return userName;
    	}
    
    	public void setUserName(String userName) {
    		this.userName = userName;
    	}
    
    	@Override
    	public int hashCode() {
    		return new HashCodeBuilder(3, 5).append(getId()).toHashCode();
    	}
    
    	@Override
    	public boolean equals(Object obj) {
    		if (this == obj) {
    			return true;
    		}
    		if (obj == null || !this.getClass().isAssignableFrom(obj.getClass())) {
    			return false;
    		}
    		User that = (User) obj;
    		if (getId() == null && that.getId() == null) {
    			return this == obj;
    		}
    		return new EqualsBuilder()
    				.append(getId(), that.getId())
    				.isEquals();
    	}
    
    	@Override
    	public User clone() {
    		User clone = new User();
    		clone.setId(this.getId());
    		clone.setUserName(this.getUserName());
    		return clone;
    	}
    }
    This kind of clone() implementation I use successfully on all SDN entities but only on @RelationshipEntitys there is build error during maven build because aspect defines final equals and hash methods. Eclipse has no problem because there is maybe different build phase. My first post on this thread was about this issue. Sample code and unit test (little bit different) are in first attachment.
    Thanks
    Vlado

  5. #5
    Join Date
    May 2012
    Posts
    107

    Default

    Vlado,

    If I understand your use case correctly, you want to use existing nodes from the database as prototypes for new nodes?

    Doesn't that then mean you can implement clone like so:

    Code:
    @Override
    	public User clone() {
    		User clone = new User();
    		clone.setUserName(this.getUserName());
    		return clone;
    	}
    And your test becomes:

    Code:
    @Test
    	public void cloneUser(){
    		User foundUser = userDao.findByPropertyValue("userName", "a");
    
    		User clone = (User) foundUser.clone();
    		assertThat(clone.getId(), equalTo(null));
    		assertThat(foundUser.getUserName(), equalTo(clone.getUserName()));
    
    		clone.setUserName("x");
    		User savedClone = userDao.save(clone);
    
    		assertThat(savedClone.getId(), notNullValue());
    		assertThat(savedClone.getUserName(), equalTo(clone.getUserName()));
    		assertThat(foundUser.getId(), not( equalTo( savedClone.getId() ) ));
    	}
    Now, your User entity will have a provided equals/ hashcode pair that only looks at identity - do you really need anything more? If so, a wrapper might be a good idea.

    Regards,

    Lasse

  6. #6
    Join Date
    Feb 2012
    Posts
    9

    Default

    Hi Lasse,
    you understood right my use case. I think that x.clone().equals(x) must be true but javadoc says that this is not an absolute requirement. For that standalone use case I can use your clone() version.

    Thanks
    Vlado

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •