Spring Boot Neo4J has no property with propertyKey="__type__" - spring

Am using spring boot 1.2.3.RELEAE and spring-data-neo4j 3.2.2.RELEASE which uses Neo4J 2.1.5.
I am trying to build a graph of Stations which are connected to other stations via "CONNECTED_TO" relationship. The relationship has "distance" as a property. Later on we are planning to do dijkstra's algorithm on the graph. But anyway... this is what we have:
#NodeEntity
#TypeAlias("Station")
public class Station {
#GraphId
private Long id;
#Indexed
public String name;
#Indexed(unique = true)
public String tlc;
public double gps_x;
public double gps_y;
#RelatedTo(type = "CONNECTED_TO", direction = OUTGOING)
public Set<Station> connectedTo = new HashSet<>();
#Fetch
#RelatedToVia(type = "CONNECTED_TO", direction = OUTGOING)
public Set<ConnectedTo> connections = new HashSet<>();
// getter + setters
}
#RelationshipEntity(type = "CONNECTED_TO")
public class ConnectedTo {
#GraphId
private Long id;
#Fetch
#StartNode
private Station fromStation;
#Fetch
#EndNode
private Station toStation;
private double distance;
// getter + setters
}
And have a stations.csv with 2K plus stations... here is a sample:
name,tlc,gps_y,gps_x
Alexandra Palace,AAP,-0.120235219,51.59792231
Achanalt,AAT,-4.913851155,57.60959445
Aberdare,ABA,-3.443083402,51.71505622
Altnabreac,ABC,-3.706280166,58.38814697
And then for the relations ships (5K plus) we have station_connections.csv.. here is a sample:
name,from_tlc,to_tlc,distance
Alexandra Palace,AAP,BOP,0.7
Alexandra Palace,AAP,HRN,0.9
Alexandra Palace,AAP,NSG,1.5
Achanalt,AAT,ACN,6.5
Achanalt,AAT,LCC,4.2
Aberdare,ABA,CMH,0.8
Altnabreac,ABC,FRS,8.1
Altnabreac,ABC,SCT,9.1
Then I have a import service to import the CSVs
Firstly, I import the stations from stations.csv. This works fine. This is the code to import it:
#Transactional
public void importStations(CsvReader stationsFile) throws IOException {
// id,terminalName,name,installed,locked,temporary,lat,lng
while (stationsFile.readRecord()) {
Station station = new Station()
.setName(stationsFile.get("name").toUpperCase())
.setTlc(stationsFile.get("tlc").toUpperCase())
.setGps_y(asDouble(stationsFile.get("gps_y")))
.setGps_x(asDouble(stationsFile.get("gps_x")));
stationRepository.save(station);
}
}
Secondly, I want to import station connections from station_connections.csv. using the following code:
#Transactional
public void importConnections(CsvReader stationsFile) throws IOException {
// name,from_tlc,to_tlc,distance
while (stationsFile.readRecord()) {
String from_tlc = stationsFile.get("from_tlc").toUpperCase();
String to_tlc = stationsFile.get("to_tlc").toUpperCase();
String distance = stationsFile.get("distance");
Station fromStation = stationRepository.findByTlc(from_tlc);
Station toStation = stationRepository.findByTlc(to_tlc);
if (fromStation != null && toStation != null) {
// need to do this get the connected stations...!!!
template.fetch(fromStation.getConnectedTo());
template.fetch(toStation.getConnectedTo());
fromStation.addStation(toStation);
template.save(fromStation);
System.out.println(from_tlc + " connected to: " + to_tlc);
}
}
}
So when it tries to import the connections I get the following error: RELATIONSHIP[4434] has no property with propertyKey="__type__".
Exception in thread "main" org.neo4j.graphdb.NotFoundException: RELATIONSHIP[4434] has no property with propertyKey="__type__".
at org.neo4j.kernel.impl.core.RelationshipProxy.getProperty(RelationshipProxy.java:195)
at org.springframework.data.neo4j.support.typerepresentation.AbstractIndexBasedTypeRepresentationStrategy.readAliasFrom(AbstractIndexBasedTypeRepresentationStrategy.java:126)
at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:36)
at
So I basically I am baffled at this error and would appreciate some help.
If there is a better way of doing this please do let me know.
GM

Can you check Relationships with id 4434 ? If it really doesn't have that property, and what kind of relationship it is.
It means that SDN couldn't load a relationship mapped to a Java type because somehow the type-information was not stored on it.
You can do that after the fact with template.postEntityCreation(rel, ConnectedTo.class);

Related

DAO class does not recognize the table name and id

I'm trying to save my images using room database.
Saving is not working. I guess my DAO class does not recognize the table name and id. Can you review the following pictures and help me to solve this issue?
#Entity(tableName = "my_images")
public class MyImagesEntity {
#PrimaryKey(autoGenerate = true)
public int image_id;
public String image_title;
public String image_description;
//images/sounds are stored in database as binary large object
public byte[] image;
//constructor
public MyImagesEntity(String image_title, String image_description, byte[] image) {
this.image_title = image_title;
this.image_description = image_description;
this.image = image;
}
}
The #Dao annotated interface
#Dao
public interface MyImagesDao {
#Insert
void insert(MyImagesEntity myImagesEntity);
#Delete
void delete(MyImagesEntity myImagesEntity);
#Update
void update(MyImagesEntity myImagesEntity);
#Query("SELECT * FROM my_images ORDER BY image_id ASC")
LiveData<List<MyImagesEntity>> getAllImages();
}
The dependencies
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
def lifecycle_version = "2.5.0-alpha03"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
// Annotation processor
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
Saving is not working.
Using the code you have supplied, I don't believe that your issue is that data is not being saved as using the following additional code :-
#Database(entities = {Data.class/*<<<<< due to test using a hijacked project*/,MyImagesEntity.class}, exportSchema = false, version = 1)
abstract class AppDatabase extends RoomDatabase {
abstract DataDao getDataDao(); //<<<<< due to test using a hijacked project
abstract MyImagesDao getMyImagesDao();
private static volatile AppDatabase instance = null;
static AppDatabase getAppDatabase(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context, AppDatabase.class,"the_database.db")
.allowMainThreadQueries()
.build();
}
return instance;
}
}
and the following in an activity:-
db = AppDatabase.getAppDatabase(this);
myImagesDao = db.getMyImagesDao();
myImagesDao.insert(new MyImagesEntity("Image001","Image 1 description",new byte[]{10,11,12,13,14,15,16,17}));
myImagesDao.insert(new MyImagesEntity("Image002","Image 2 description",new byte[]{0,1,2,3,4,5,6,7,8,9,}));
for(MyImagesEntity mie: myImagesDao.getAllImagesOther()) {
Log.d("DBINFO","Image is " + mie.image_description + " Description is " + mie.image_description);
}
with getAllImagesOther being (without LiveData) :-
#Query("SELECT * FROM my_images ")
List<MyImagesEntity> getAllImagesOther();
Then the result is :-
D/DBINFO: Image is Image 1 description Description is Image 1 description
D/DBINFO: Image is Image 2 description Description is Image 2 description
and using App Inspection you can see that the data has been added :-
Therefore I believe that you issues is with how your are observing (or not) the LiveData. I'd suggest using App Inspection to see if the data is being saved.

Implementing findOrCreate strategy with spring data in concurrent environment

I'm facing the following problem:
I have an entity:
#Table(name = "host",
uniqueConstraints =
{
#UniqueConstraint(name = "uq_host_0",
columnNames = {"orgName", "hostName"})}
)
class Host {
private String id;
private String hostName;
private String orgName;
//gets
//sets
//constructors
//...
}
This entity has uniqueness constrain on orgName + hostName fields.
And corresponding Repository for the entity:
public interface HostRepository extends JpaRepository<Host, String> {
Page<Host> findByOrgId(String orgId, Pageable pageable);
Host findOneByOrgNameIdAndId(String orgName, String id);
Host findOneByOrgNameAndHostName(String orgName, String hostName);
//..
}
I want to create a service with findOrCreate method that would:
Create new Host in case if Host does not exist
If Host does exist, return the Host
Taking into account uniqueness constraint on hostName + orgName fields.
This method should work in assumption that it can be executed on multiple different instances of the same application as well as in different threads.
Currently I came up with two solutions:
Use separate method for creation with Propagation = RequiresNew
#Service
public class HostService {
#Autowired
private HostRepository hostRepository;
#Transactional
public Host findOrCreate(Host host) {
try {
return create(host);
} catch(ConstraintViolationException e) {
//means the host has already been created by other transaction
return hostRepository.findFirstByOrgNameAndHostName(host.getHostName(), host.getOrgName());
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Host create(Host host) {
//constraint violation may be thrown
hostRepository.save(host);
}
}
Perform all the logic in one method, but with isolation level = serializable:
#Service
public class HostService {
#Autowired
private HostRepository hostRepository;
#Transactional(isolation = Isolation.SERIALIZABLE)
public Host findOrCreate(Host host) {
Optional<Host> existing = Optional.ofNullable(hostRepository.findOneByOrgNameAndHostName(host.getOrgName(), host.getHostName()));
if(existing.isPresent()) {
return existing;
}
return hostRepository.save(host);
}
}
It seems to me that both of these options would work in concurrent environment and the first option is preferable because works faster. However, I'm afraid that I may miss underwater rocks.
Did anyone face the problem before?
If so, I would really grateful for any advice or alternative solutions apart from listed above,
Thanks, cheers
I have an idea.
You can define one method in HostRepository like:
Optional<Host> findByOrgNameAndHostName(String orgName, String hostName)
And in HostService, you can use the method:
Host host = hostRepository.findByOrgNameAndHostName("hoge", "fuga")
.orElseGet(() -> hostRepository.save(new Host("hoge", "fuga")))

Cannot Query Neo4j Repositories

Hey everyone I am fairly new to Neo4j and am having an issue querying my repositories.
Repository is the follow:
public interface NodeOneRepository extends GraphRepository<NodeOne> {
List<NodeOne> findByNodeTwoNodeThreeAndActiveTrue(NodeThree nodeThree);
}
My entities are the following:
#NodeEntity(label = "NodeOne")
public class NodeOne {
#GraphId
private Long id;
private Boolean active = TRUE;
#Relationship(type = "IS_ON")
private NodeTwo nodeTwo;
}
#NodeEntity(label = "NodeTwo")
public class NodeTwo {
#GraphId
private Long id;
#Relationship(type = "CONTAINS", direction = "INCOMING")
private NodeThree nodeThree;
#Relationship(type = "IS_ON", direction = "INCOMING")
private List<NodeOne> nodeOnes = new ArrayList<>();
}
#NodeEntity(label = "NodeThree")
public class NodeThree {
#GraphId
private Long id;
#Relationship(type = "CONTAINS")
private List<NodeTwo> nodeTwos = new ArrayList<>();
}
Getters & Setters omitted. When I call the method I get an empty list. Is there something I am doing incorrectly?
You didn't describe exactly what you wanted to achieve, but I can see two problems:
Problem 1:
The current version of Spring Data Neo4j and OGM only allow nested finders, that is, finders that specify a relationship property, to one depth.
Supported
findByNodeTwoSomePropertyAndActiveTrue(String relatedNodePropertyValue)
Not Supported
findByNodeTwoNodeThree //Nesting relationships in finders is not supported
Problem 2:
Derived Finders Allow Matching Properties and Nested Properties. Not a whole instance of that class.
You can probably achieve what you would like using a custom query.
#Query("custom query here")
List<NodeOne> findByNodeTwoNodeThreeAndActiveTrue(NodeThree nodeThree);
If you need help to write a custom query, you can post another question or join the neo4j-users public slack channel.

spring jpa projection nested bean

is it possible to have a projection with nested collection with Spring JPA?
I have the following 2 simple entity (to explain the problem)
#Entity
#Table(name = "person")
public class Person implements Serializable {
private Integer id;
private String name;
#OneToMany
private List<Address> addressList = new ArrayList<>();
}
#Entity
#Table(name = "address")
public class Address implements Serializable {
private Integer id;
private String city;
private String street;
}
Is it possible to have a projection of Person with following attributes filled in ? {person.name, address.city}
I might be wrong in semantics of word Projection. but the problem is what i need to achieve. Maybe it is not possible with Projection, but is there another way to achieve the end goal? Named Entity graph perhaps ?
P.S. please suggest a solution for Spring JPA not Spring Jpa REST
thanks in advance
You're right, Entity Graphs serve this exact purpose - control field loading.
Create entity graphs dynamically from the code or annotate target entities with Named Entity Graphs and then just use their name.
Here is how to modify your Person class to use Named Entity Graphs:
#Entity
#Table(name = "person")
#NamedEntityGraph(name = "persion.name.with.city",
attributeNodes = #NamedAttributeNode(value = "addressList", subgraph = "addresses.city"),
subgraphs = #NamedSubgraph(name = "addresses.city", attributeNodes = #NamedAttributeNode("city")))
public class Person implements Serializable {
private Integer id;
private String name;
#OneToMany
private List<Address> addressList;
}
And then when loading your person:
EntityGraph graph = em.getEntityGraph("person.name.with.city");
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);
return em.find(Person.class, personId, hints);
The same applies for queries, not only em.find method.
Look this tutorial for more details.
I think that that's not usual scenario of Data JPA usage. But you can achieve your goal with pure JPQL:
SELECT a.street, a.person.name FROM Address a WHERE …
This solution has 2 drawbacks:
It forces you to have bidirectional relationship Address ←→ Person
It returns List
Another solution (and that's preferred JPA way) is to create DTO like this:
class MyPersonDTO {
private String personName;
private List<String> cities;
public MyPersonDTO(String personName, List<Address> adresses) {
this.personName = personName;
cities = adresses
.stream()
.map(Address::getCity)
.collect(Collectors.toList());
}
}
And the execute JPQL query like this:
SELECT NEW package.MyPersonDTO(p.name, p.addressList) FROM Person p WHERE …
Return type will be List<MyPersonDTO> in that case.
Of course you can use any of this solutions inside #Query annotation and it should work.

How to persist relationships between Neo4J NodeEntitys in Spring Data Graph without calling persist twice

The test below fails if I remove the first persist(). Why do I need to persist the NodeEntity in order for the Set to be instantiated? Is there some better way to do this? I don't want to have to write to the database more often than nessesary.
#Test
public void testCompetenceCreation() {
Competence competence = new Competence();
competence.setName("Testcompetence");
competence.persist(); //test fails if this line is removed
Competence competenceFromDb = competenceRepository.findOne(competence.getId());
assertEquals(competence.getName(), competenceFromDb.getName());
Education education = new Education();
education.setName("Bachelors Degree");
competence.addEducation(education);
competence.persist();
assertEquals(competence.getEducations(), competenceFromDb.getEducations());
}
If i remove the mentioned line, the exception bellow occurs:
Throws
java.lang.NullPointerException
at com.x.entity.Competence.addEducation(Competence.java:54)
Competence.class:
#JsonIgnoreProperties({"nodeId", "persistentState", "entityState"})
#NodeEntity
public class Competence {
#RelatedTo(type = "EDUCATION", elementClass = Education.class)
private Set<Education> educations;
public Set<Education> getEducations() {
return educations;
}
public void addEducation(Education education) {
this.educations.add(education);
}
}
Education.class
#JsonIgnoreProperties({"nodeId", "persistentState", "entityState"})
#NodeEntity
public class Education {
#GraphId
private Long id;
#JsonBackReference
#RelatedTo(type = "COMPETENCE", elementClass = Competence.class, direction = Direction.INCOMING)
private Competence competence;
#Indexed
private String name;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
What version of SDN are you running?
Because up until the first persist the entity is detached and AJ doesn't take care of the fields (like creating the managed set). Persist creates the node at connects it to the entity, from then on until the transaction commits your entity is attached and all the changes will be written through.
It only writes to the db at commit, so no worries about too many writes. All the other changes will just be held in memory for your transaction. Probably you should also annotate the test method with #Transactional.
Can you create a JIRA issue for this? So that a consistent handling is provided. (Problem being that it probably also complains when you initialize the set yourself.)
Two other things:
as your relationship between Education<--Competence is probably the same and should just be navigated in the other direction you must provide the same type name in the annotation.
e.g. Education<-[:PROVIDES]-Competence
also if you don't call persist your entity will not be created and then the findOne by returning null

Resources