I have a problem with Spring Data Neo4j and OGM. When I create a node for first time, it`s ok but if I refresh that page I get this error:
Cypher execution failed with code 'Neo.ClientError.Schema.ConstraintValidationFailed': Node(126) already exists with label Country and property name = 'Country-1'.
I searched the web and read so many documents about equals and hashCode but none of them is helping. Here are my classes:
public abstract class Place {
#Id
#GeneratedValue(strategy = UuidStrategy.class)
private String id ;
private String name ;
public String getId(){return id ;}
}
#Getter
#Setter
#NodeEntity(label = "Country")
#CompositeIndex(unique = true , properties = "name")
public class Country extends Place {
private String label = "Country";
public Country() {}
#Override
public int hashCode() {
int result = label == null ? 1 : label.hashCode();
result = 31 * result + this.getName() == null ? 0 : this.getName().hashCode();
result = 31 * result + this.getId() == null ? 0 : this.getId().hashCode();
return result;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Country)) {
return false;
}
Country that = (Country) o ;
return this.getName().equals(that.getName())
&& this.getId().equals(that.getId())
&& this.getLabel().equals(that.getLabel());
}
}
Repository is default. As I know it`s a problem of equality check but how can I fix this?
You created a constraint on your Country entity by defining
#CompositeIndex(unique = true , properties = "name")
and probably you also enabled the auto index manager feature in the Neo4j-OGM SessionFactory configuration.
This is not related to any implementation of hashCode or equals.
This will also explain the behaviour you are facing: First run succeeds but the very same action repeated failed.
Related
I've got an object that looks like this
import lombok.Data;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
import java.util.HashSet;
import java.util.Set;
#Data
#NodeEntity
public class GPlayer {
#GraphId
private Long id;
#Relationship(type = "comrade", direction = Relationship.UNDIRECTED)
private Set<GPlayer> comrades;
// #Indexed(unique = true) doesn't work in v4
private String name;
/**
* Adds new comrade.
*
* #param comrade comrade
*/
public void acquainted(GPlayer comrade) {
if (null == comrades) {
comrades = new HashSet<>();
} else {
if (comrades.contains(comrade)) {
return;
}
}
comrades.add(comrade);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
GPlayer gPlayer = (GPlayer) o;
if (!id.equals(gPlayer.id)) return false;
return name.equals(gPlayer.name);
}
#Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + id.hashCode();
result = 31 * result + name.hashCode();
return result;
}
#Override
public String toString() {
return "GPlayer{" +
"id=" + id +
", comrades=" + comrades +
", name='" + name + '\'' +
'}';
}
}
I want to store it in Neo4j with unique name. So when I insert GPlayer - if the object with such name doesn't exist I don't want to insert it.
I have repository that looks like this
public interface GraphPlayerRepository extends GraphRepository<GPlayer> {
List<GPlayer> findAll();
GPlayer findByName(String name);
}
To insert I do like this
private Long createPlayer(String playerName, String comrade, GraphPlayerRepository gRepo) {
GPlayer gPlayer = gRepo.findByName(playerName);
if (null == gPlayer) {
gPlayer = new GPlayer();
gPlayer.setName(playerName);
if (null != comrade) {
gPlayer.acquainted(gRepo.findByName(comrade));
}
gRepo.save(gPlayer);
LOGGER.info("Created new GRAPH player: {}", gPlayer);
} else {
gPlayer.acquainted(gRepo.findByName(comrade));
gRepo.save(gPlayer);
LOGGER.info("Updated player: {}", gPlayer);
}
return gPlayer.getId();
}
But it looks rather verbose. Is there a way to make it simpler?
You could use the name as the id of the entity
#Index(unique=true, primary=true)
private String name;
Then no need to declare the findByName method, just use Neo4jRepository
public interface GraphPlayerRepository extends Neo4jRepository<GPlayer, String> {
...
}
and use repository.findOne(name) to get players from the name.
That said, the implementation of hashCode and Equals is incorrect : it is strongly advised not to use the Long id in these. See here.
Otherwise, not related to SDN, but improvements could include :
use lombok to generate hashcode and equals
initialize the hashSet at the field level
You could also migrate to SDN 5 and make use of Optional return types to avoid the if/else nullity check blocks.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 7 years ago.
Improve this question
In my Neo4j/Neo4j Spring Data application I have a following entities:
VoteGroup contains relationships VOTED_ON and VOTED_FOR to entities Criterion and Decision and list of Vote
#NodeEntity
public class VoteGroup extends BaseEntity {
private static final String VOTED_ON = "VOTED_ON";
private final static String VOTED_FOR = "VOTED_FOR";
private final static String CONTAINS = "CONTAINS";
#GraphId
private Long id;
#RelatedTo(type = VOTED_FOR, direction = Direction.OUTGOING)
private Decision decision;
#RelatedTo(type = VOTED_ON, direction = Direction.OUTGOING)
private Criterion criterion;
#RelatedTo(type = CONTAINS, direction = Direction.OUTGOING)
private Set<Vote> votes = new HashSet<>();
private double avgVotesWeight;
private long totalVotesCount;
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
VoteGroup voteGroup = (VoteGroup) o;
if (id == null)
return super.equals(o);
return id.equals(voteGroup.id);
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : super.hashCode();
}
.....
}
Vote entity looks like:
#NodeEntity
public class Vote extends BaseEntity {
private final static String CONTAINS = "CONTAINS";
private final static String CREATED_BY = "CREATED_BY";
#GraphId
private Long id;
#RelatedTo(type = CONTAINS, direction = Direction.INCOMING)
private VoteGroup group;
#RelatedTo(type = CREATED_BY, direction = Direction.OUTGOING)
private User author;
private double weight;
....
}
public class BaseEntity {
private Date createDate;
private Date updateDate;
public BaseEntity() {
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
}
also. I use Neo4j hook based on BaseEntity:
#Configuration
#EnableNeo4jRepositories(basePackages = "com.example")
#EnableTransactionManagement
public class Neo4jConfig extends Neo4jConfiguration implements BeanFactoryAware {
...
/**
* Hook into the application lifecycle and register listeners that perform
* behaviour across types of entities during this life cycle
*
*/
#Bean
protected ApplicationListener<BeforeSaveEvent<BaseEntity>> beforeSaveEventApplicationListener() {
return new ApplicationListener<BeforeSaveEvent<BaseEntity>>() {
#Override
public void onApplicationEvent(BeforeSaveEvent<BaseEntity> event) {
BaseEntity entity = event.getEntity();
if (entity.getCreateDate() == null) {
entity.setCreateDate(new Date());
} else {
entity.setUpdateDate(new Date());
}
}
};
}
...
}
in order to make a vote, I have implemented following method VoteGroupDaoImpl.createVote:
#Service
#Transactional
public class VoteGroupDaoImpl implements VoteGroupDao {
#Autowired
private VoteRepository voteRepository;
#Autowired
private VoteGroupRepository voteGroupRepository;
#Override
public Vote createVote(Decision decision, Criterion criterion, User author, String description, double weight) {
VoteGroup voteGroup = getVoteGroupForDecisionOnCriterion(decision.getId(), criterion.getId());
if (voteGroup == null) {
voteGroup = new VoteGroup(decision, criterion, weight, 1);
} else {
long newTotalVotesCount = voteGroup.getTotalVotesCount() + 1;
double newAvgVotesWeight = (voteGroup.getAvgVotesWeight() * voteGroup.getTotalVotesCount() + weight) / newTotalVotesCount;
voteGroup.setAvgVotesWeight(newAvgVotesWeight);
voteGroup.setTotalVotesCount(newTotalVotesCount);
}
voteGroup = voteGroupRepository.save(voteGroup);
return voteRepository.save(new Vote(voteGroup, author, weight, description));
}
...
}
and
#Repository
public interface VoteGroupRepository extends GraphRepository<VoteGroup>, RelationshipOperationsRepository<VoteGroup> {
#Query("MATCH (d:Decision)<-[:VOTED_FOR]-(vg:VoteGroup)-[:VOTED_ON]->(c:Criterion) WHERE id(d) = {decisionId} AND id(c) = {criterionId} RETURN vg")
VoteGroup getVoteGroupForDecisionOnCriterion(#Param("decisionId") Long decisionId, #Param("criterionId") Long criterionId);
}
Right now, method VoteGroupDaoImpl.createVote works really slow with a huge latency .. what can be a reason of that ?
ADDED PROFILE output
for
MATCH (d:Decision)<-[:VOTED_FOR]-(vg:VoteGroup)-[:VOTED_ON]->(c:Criterion) WHERE id(d) = {decisionId} AND id(c) = {criterionId} RETURN vg
Cypher version: CYPHER 2.2, planner: COST. 33 total db hits in 181 ms.
PROFILE Java code:
Rich profiler information:
HTML page with profiler information
Some ideas that may help:
Execute the query:
MATCH (d:Decision)<-[:VOTED_FOR]-(vg:VoteGroup)-[:VOTED_ON]->(c:Criterion) WHERE id(d) = {decisionId} AND id(c) = {criterionId} RETURN vg
from the web interface or the console and check how it behaves. Try for the same ids you use in the app. Check what is the execution time.
Has VoteGroup numerous relations to Votes? If yes, can you remove:
#RelatedTo(type = CONTAINS, direction = Direction.OUTGOING)
private Set<Vote> votes = new HashSet<>();
and keep information about relation on the Vote side only? Can you check the performance after that change?
Can you use some kind of a profiler tool to identify the exact place of performance problems? Right now it may be still difficult to guess...
Does the code behave as it should? Don you have any duplicates in the DB? Maybe you have bugs in your hashCode/equals methods that cause much more changes in the DB than there really should be?
You could try to reformulate the getVoteGroupForDecisionOnCriterion query as follows, in order to avoid the cartesian product:
MATCH (d:Decision) WHERE id(d) = {decisionId}
WITH d MATCH (c:Criterion) WHERE id(c) = {criterionId}
WITH d,c MATCH d<-[:VOTED_FOR]-(vg:VoteGroup)-[:VOTED_ON]->c
RETURN vg
I'm moved to new Neo4j 2.2.4 and SDN 3.4.0.RC1 and the issue disappeared
I am new to the dependency injection pattern and I am having issues getting a new instance of a class from container.Resolve in tinyioc it just keeps returning the same instance rather than a new instance. Now for the code
public abstract class HObjectBase : Object
{
private string _name = String.Empty;
public string Name
{
get
{
return this._name;
}
set
{
if (this._name == string.Empty && value.Length > 0 && value != String.Empty)
this._name = value;
else if (value.Length < 1 && value == String.Empty)
throw new FieldAccessException("Objects names cannot be blank");
else
throw new FieldAccessException("Once the internal name of an object has been set it cannot be changed");
}
}
private Guid _id = new Guid();
public Guid Id
{
get
{
return this._id;
}
set
{
if (this._id == new Guid())
this._id = value;
else
throw new FieldAccessException("Once the internal id of an object has been set it cannot be changed");
}
}
private HObjectBase _parent = null;
public HObjectBase Parent
{
get
{
return this._parent;
}
set
{
if (this._parent == null)
this._parent = value;
else
throw new FieldAccessException("Once the parent of an object has been set it cannot be changed");
}
}
}
public abstract class HZoneBase : HObjectBase
{
public new HObjectBase Parent
{
get
{
return base.Parent;
}
set
{
if (value == null || value.GetType() == typeof(HZoneBase))
{
base.Parent = value;
}
else
{
throw new FieldAccessException("Zones may only have other zones as parents");
}
}
}
private IHMetaDataStore _store;
public HZoneBase(IHMetaDataStore store)
{
this._store = store;
}
public void Save()
{
this._store.SaveZone(this);
}
}
And the derived class is a dummy at the moment but here it is
public class HZone : HZoneBase
{
public HZone(IHMetaDataStore store)
: base(store)
{
}
}
Now since this is meant to be an external library I have a faced class for accessing
everything
public class Hadrian
{
private TinyIoCContainer _container;
public Hadrian(IHMetaDataStore store)
{
this._container = new TinyIoCContainer();
this._container.Register(store);
this._container.AutoRegister();
}
public HZoneBase NewZone()
{
return _container.Resolve<HZoneBase>();
}
public HZoneBase GetZone(Guid id)
{
var metadataStore = this._container.Resolve<IHMetaDataStore>();
return metadataStore.GetZone(id);
}
public List<HZoneBase> ListRootZones()
{
var metadataStore = this._container.Resolve<IHMetaDataStore>();
return metadataStore.ListRootZones();
}
}
However the test is failing because the GetNewZone() method on the Hadrian class keeps returning the same instance.
Test Code
[Fact]
public void ListZones()
{
Hadrian instance = new Hadrian(new MemoryMetaDataStore());
Guid[] guids = { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
int cnt = 0;
foreach (Guid guid in guids)
{
HZone zone = (HZone)instance.NewZone();
zone.Id = guids[cnt];
zone.Name = "Testing" + cnt.ToString();
zone.Parent = null;
zone.Save();
cnt++;
}
cnt = 0;
foreach (HZone zone in instance.ListRootZones())
{
Assert.Equal(zone.Id, guids[cnt]);
Assert.Equal(zone.Name, "Testing" + cnt.ToString());
Assert.Equal(zone.Parent, null);
}
}
I know its probably something simple I'm missing with the pattern but I'm not sure, any help would be appreciated.
First, please always simplify the code to what is absolutely necessary to demonstrate the problem, but provide enough that it will actually run; I had to guess what MemoryMetaDataStore does and implement it myself to run the code.
Also, please say where and how stuff fails, to point others straight to the issue. I spent a few minues figuring out that the exception I was getting was your problem and you weren't even getting to the assertions.
That said, container.Resolve<HZoneBase>() will always return the same instance because that's how autoregistration in TinyIoC works - once an abstraction has been resolved, the same instance is always returned for subsequent calls.
To change this, add the following line to the Hadrian constructor:
this._container.Register<HZoneBase, HZone>().AsMultiInstance();
This will tell the container to create a new instance for each resolution request for HZoneBase.
Also, Bassetassen's answer about the Assert part is correct.
In general, if you want to learn DI, you should read Mark Seemann's excellent book "Dependency Injection in .NET" - not quite an easy read as the whole topic is inherently complex, but it's more than worth it and will let you get into it a few years faster than by learning it on your own.
In your assert stage you are not incrementing cnt. You are also using the actual value as the expected one in the assert. This will be confusing, becuase it says something is excpected when it actually is the actual value that is returned.
The assert part should be:
cnt = 0;
foreach (HZone zone in instance.ListRootZones())
{
Assert.Equal(guids[cnt], zone.Id);
Assert.Equal("Testing" + cnt.ToString(), zone.Name);
Assert.Equal(null, zone.Parent);
cnt++;
}
Im using vaadin's TreeTable and im trying to add tooltips for my rows. This is how they say it should be done but the propertyId is always null so i cant get the correct column? And yes i'v run this in eclipse debugger aswell =)
Code related to this part:
private void init() {
setDataSource();
addGeneratedColumn("title", new TitleColumnGenerator());
addGeneratedColumn("description", new DescriptionGenerator());
setColumnExpandRatios();
setItemDescriptionGenerator(new TooltipGenerator());
}
protected class TooltipGenerator implements ItemDescriptionGenerator{
private static final long serialVersionUID = 1L;
#Override
public String generateDescription(Component source, Object itemId, Object propertyId) {
TaskRow taskRow = (TaskRow)itemId;
if("description".equals(propertyId)){
return taskRow.getDescription();
}else if("title".equals(propertyId)){
return taskRow.getTitle();
}else if("category".equals(propertyId)){
return taskRow.getCategory().toString();
}else if("operation".equals(propertyId)){
return taskRow.getOperation().toString();
}else if("resourcePointer".equals(propertyId)){
return taskRow.getResourcePointer();
}else if("taskState".equals(propertyId)){
return taskRow.getTaskState().toString();
}
return null;
}
}
I have passed the source object as the itemId when adding an item to the tree.
Node node = ...;
Item item = tree.addItem(node);
this uses the object "node" as the id. Which then allows me to cast itemId as an instance of Node in the generateDescription method.
public String generateDescription(Component source, Object itemId, Object propertyId) {
if (itemId instanceof Node) {
Node node = (Node) itemId;
...
Maybe not the best solution, but it Works for me. Then again, I am adding items directly to the tree rather than using a DataContainer.
I need to perform some initialization when new instances of my domain class are created.
class ActivationToken {
String foo
String bar
}
When I do this I want bar to be initialized by code inside ActivationToken:
def tok = new ActivationToken(foo:'a')
I cannot see how to 'override' the 'constructor' to make this happen. I know in this case I could just add a normal constructor but this is just a simple example.
The map constructor is coming from Groovy - not Grails in this case. I did some experimentation, and this is what I came up with:
class Foo {
String name = "bob"
int num = 0
public Foo() {
this([:])
}
public Foo(Map map) {
map?.each { k, v -> this[k] = v }
name = name.toUpperCase()
}
public String toString() {
"$name=$num"
}
}
assert 'BOB=0' == new Foo().toString()
assert 'JOE=32' == new Foo(name:"joe", num: 32).toString()
Basically, it appears that you'll have to manually override the constructors if you need to process the property after construction.
Alternately, you can override individual setters, which is cleaner and safer in general:
class Foo {
String name = "bob"
int num = 0
public void setName(n) {
name = n.toUpperCase()
}
public String toString() {
"$name=$num"
}
}
assert 'bob=0' == new Foo().toString()
assert 'JOE=32' == new Foo(name:"joe", num: 32).toString()
Note that the default value isn't processed, but that should be OK in most instances.
The solution above is also good for cases where initializing an object from parameters in a web request, for example, where you wish to ignore extraneous values, catching Missing property exceptions.
public Foo(Map map) {
try {
map?.each { k, v -> this[k] = v }
}
catch(Exception e){
}
}