Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
05-19-2020 11:36 AM
I have some code that within a transaction, creates a node and a list of edges and calls save(). I then query for the list of edges by a non-keyed field hoping to return one edge, however, the repository returns all the edges. Why is that? I have to call session.clear() before calling my repository query to get the filter to work properly. Is this because of the session cache or something?
@SpringBootTest()
@TestInstance(Lifecycle.PER_CLASS)
@ActiveProfiles(profiles = "embeddedTest")
public class UpdateEdgeTest {
@Autowired
private GraphRepositoryTestingHelper graphRepositoryTestingHelper;
@Autowired
private StudentRepository studentRepository;
@Autowired
private CourseRepository courseRepository;
@Autowired
private Session session;
public void setupData(){
Student expected = new Student();
expected.setFirstName("John");
expected.setLastName("Carter");
expected.setKey("naturalKey");
List<EnrolledIn> enrollment = new ArrayList<>();
EnrolledIn enrolledIn = new EnrolledIn()
.setCourse(new Course()
.setCourseName("neo4j101")
.setKey("n4j101-jan2020"))
.setSemester("Jan-2020")
.setStudent(expected);
EnrolledIn enrolledIn2 = new EnrolledIn()
.setCourse(new Course()
.setCourseName("java101")
.setKey("java101-2020"))
.setSemester("Jan-2020")
.setStudent(expected);
enrollment.add(enrolledIn);
enrollment.add(enrolledIn2);
expected.setEnrolledIn(enrollment);
final Student actual = studentRepository.save(expected);
}
@AfterAll //must use @AfterAll or else the nodes won't be deleted
public void after(){
graphRepositoryTestingHelper.deleteAllNodes();
}
@Test
@Transactional
public void shouldUpdateOneCourseAndAddNewOneLeavingExistingOnesUnchanged(){
// setupData() does not work properly.... for some reason attempting to save this data
// and then call the query findStudentForCourse within the same transaction will not filter out the enrolledIn edges.
setupData();
final Optional<Student> astudentWithTwoEdges = studentRepository.findById("naturalKey");
assertEquals(2, astudentWithTwoEdges.get().getEnrolledIn().size(), "should have 2 courses now");
// to fix, call session.clear().... not sure why.
//calling this makes the below query 'findStudentForCourse' work properly... I have no idea why.
//if this is not called, the query returns ALL the enrolledIn relationships......
session.clear();
//find one of the student edges
final Student studentForCourse = studentRepository.findStudentForCourse("naturalKey", "java101-2020");
assertEquals(1, studentForCourse.getEnrolledIn().size(), "should only be one due to query filter");
}
@Repository
public interface StudentRepository extends Neo4jRepository<Student, String> {
@Query("MATCH (student:Student { key: {key}})-[e:ENROLLED_IN]->(c:Course) "
+ "WHERE c.key = {`enrollmentId`} "
+ "RETURN student, e, c")
Student findStudentForCourse(@Param("key") String key, @Param("enrollmentId") String enrollmentId);
}
05-26-2020 11:28 PM
You are absolutely right with your assumption.
Within one transaction one Session
will get created with a caching mechanism. When you query for the very same Student
, Neo4j-OGM will just add / modify the data on the loaded object but not discard any that is not included in the result.
You could inject the SessionFactory
instead of the Session
and explicitly open a new Session
for the test data setup.
ps. I have the feeling that I answered this somewhere else already around last week.
05-28-2020 07:55 AM
I tried opening a new session for the test data setup but it still didn't do what I expected. The unit test method is decorated with @Transactional. So I made a new method to be called in a @before setup() method. My query still returned more results than expected, suggesting that the unit test session is still getting data from the call to .save() that was made in the @before setup() method. How can I separate the session? remove @Transactional? (but what if I need a transaction?)
I moved this to a @before method and it's not giving what i'd expect. Is this correct?
Session session = sessionFactory.openSession();
try(Transaction tx = session.beginTransaction())
{
final Student actual = studentRepository.save(expected);
}
I also attempted to run that code above in the unit test and received:
java.lang.IllegalStateException: Not allowed to create transaction on shared Session - use Spring transactions instead
Edit3: the shared session is the one that was @autowired. Apparently using it to create a new transaction isn't allowed. I was however able to use the sessionFactory to create a new session and use that new session to make a new transaction. 😄
05-28-2020 10:44 AM
How can I separate the sessions? It's still using a common session despite creating a new session.
@Transactional
public void shouldUpdateOneCourseAndAddNewOneLeavingExistingOnesUnchanged() {
Student studentForCourse;
session2 = sessionFactory.openSession();
try (Transaction tx = session2.beginTransaction()) {
setupData();
}
studentForCourse = studentRepository.findStudentForCourse("naturalKey", "java101-2020");
//this still fails. It returns 2 courses instead of 1.
assertEquals(1, studentForCourse.getEnrolledIn().size(), "should only be one due to query filter");
}
06-07-2020 04:21 AM
I think the query is not properly formatted and "WHERE c.key = {enrollmentId
} " is getting bypassed and returning all rows instead of specific one.So please read the Cypher Query Language Reference card for proper syntax.
All the sessions of the conference are now available online