Spring #Transactional is not commited. Neo4J - spring

I have an entity User that has relationship WORKS_FOR with an entity Organization. Organization has relationship HAS_EMPLOYEE with all users that are in and a relationship HAS_ANCHOR, with one anchor for the whole organization to manage it. I am trying to update organization entity with another user from "HAS_EMPLOYEE" list to become a new anchor. But there are no changes in db after the method and no runtime exceptions are thrown.
#Transactional
public OrganizationDTO changeAnchorForOrganization(UUID prevAnchorId, UUID newAnchorId) {
User newAnchor = userService.getAnyUserById(newAnchorId);
if (!newAnchor.isActive()) {
throw new BadRequestException(ExceptionType.REQUEST_BODY_INVALID);
}
User prevAnchor = userService.getAnyUserById(prevAnchorId);
Organization organization = getOrganizationByAnchorId(prevAnchorId);
Set<String> prevAnchorPermissions = prevAnchor.getPermissions();
prevAnchorPermissions.remove(SubRolesConstants.anchor);
prevAnchor.setPermissions(prevAnchorPermissions);
Set<String> newAnchorPermissions = newAnchor.getPermissions();
newAnchorPermissions.add(SubRolesConstants.anchor);
newAnchor.setPermissions(newAnchorPermissions);
organization.setAnchor(newAnchor);
return organizationMapper.entityToDTO(organization);
}
organization.setAnchor(newAnchor); this line is not working?
The result DTO has the changes made to org anchor but db is not. And if i'll try to get the ogranization after this method i'll get the old version of organization(with previous anchor)
Stuck with that for a long time. Maybe somebody can help me?

I was missing organizationRepository.save(organization).I think it's because of neo4j because by default #Transactional annotation commit any changes made to entities at the end of the service call. Or it's just a bug.

Related

Is double saving a new entity instance with a Spring data 2 JpaRepository correct?

I have two entities in a bi-directional many to many relationship.
A <-> many to many <-> B
I have an endpoint where a client can create an instance of A, and at the same time add some number of B entities to that A, by passing in an array of B entity id keys. Please keep in mind that these B entities already exist in the database. There is no business or software design case for tightly coupling their creation to the creation of A.
So class A looks like this, and B is the same, but with references to A.
#Entity
class A {
#Id
#GeneratedValue
int id;
#ManyToMany
List<B> bs;
String someValue;
int someValue2;
// With some getters and setters omitted for brevity
}
So at first try my endpoint code looks like this.
public A createA(#RequestBody A aToCreate) {
A savedA = aRepository.save(aToCreate);
savedA.getbs().forEach(b -> Service.callWithBValue(b.getImportantValue());
}
And the client would submit a JSON request like this to create a new A which would contain links to B with id 3, and B with id 4.
{
"bs": [{id:3}, {id:10}],
"someValue": "not important",
"someValue2": 1
}
Okay so everything's working fine, I see all the fields deserializing okay, and then I go to save my new A instance using.
aRepository.save(aToCreate);
And that works great... except for the fact that I need all the data associated with the b entity instances, but the A object returned by aRepository.save() has only populated the autofill fields on A, and done nothing with the B entities. They're still just hollow entities who only have their ids set.
Wut.
So I go looking around, and apparently SimpleJpaRepository does this.
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
And since the A entity is brand new, it only persists the A entity, but it doesn't merge it so I don't get any of the rich B data. So okay, if I modify my code to take this into account I get this.
public A createA(#RequestBody A aToCreate) {
A savedA = aRepository.save(aRepository.save(aToCreate));
savedA.getbs().forEach(b -> Service.callWithBValue(b.getImportantValue());
}
Which works just fine. The second pass through the repository service it merges instead of persists, so the B relationships get hydrated.
My question is: Is this correct, or is there something else I can do that doesn't look so ineloquent and awful?
To be clear this ONLY matters when creating a brand new instance of A, and once A is in the database, this isn't an issue anymore because the SimpleJpaRepository will flow into the em.merge() line of code. Also I have tried different CascadingType annotations on the relationship but none of them are what I want. Cascading is about persisting the state of the parent entity's view of its children, to its children, but what I want to do is hydrate the child entities on new instance creation, instead of having to make two trips to the database.
In the case of a new A, aToCreate and savedA are the same instance because that is what the JPA spec madates:
https://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#persist(java.lang.Object)
Make an instance managed and persistent.
Spring Data simply returns the same instance so persist/merge can be abstracted into one method.
If the B instances you wish to associate with A are existing entities then you need to fetch a reference to these existing instances and set them on A. You can do this without a database hit by using the T getOne(ID id) method of Spring Data's JpaRepository:
https://docs.spring.io/spring-data/jpa/docs/2.1.4.RELEASE/api/
You can do this in your controller or possibly via a custom deserializer.
This is what I ended up going with. This gives the caller the ability to save and hydrate the instance in one call, and explains what the heck is going on. All my Repository instances now extend this base instance.
public interface BaseRepository<T, ID> extends JpaRepository<T, ID> {
/**
* Saves an instance twice so that it's forced to persist AND then merge. This should only be used for new detached entities that need to be saved, and who also have related entities they want data about hydrated into their object.
*/
#Transactional
default T saveAndHydrate(T save) {
return this.save(this.save(save));
}
}

Does Entity Framework need a Session HttpContext on CRUD?

I've watched some class about Entity Framework with MySql and Sql Server.
First the teacher uses the Entity from a database, where he creates the context DB and than he start the insert
using(sampleEntities ctx = new sampleEntities()){
client clt = new client();
clt.name = txtName.Text;
clt.phone = txtPhone.Text;
ctx.Add(clt);
ctx.SaveChanges();
But other teacher does something different with DAL, BLL and UI usgin session and httpContext, he says Entity needs this Session to avoid "persistence conflict" since the first example is using the same "connection/session" for lots of users, so that is what he does:
public static sample01Entities Current
{
get
{
if (System.Web.HttpContext.Current.Session["SampleDbContext"] == null)
{
db = new sample01Entities();
System.Web.HttpContext.Current.Session["SampleDbContext"] = db;
}
return db;
}
}
`
and then in Dalcity
public void Add(cidade c)
{
SampleDbContext.Current.cidade.Add(c);
SampleDbContext.Current.SaveChanges();
SampleDbContext.Current.ChangeTracker.Entries<cidade>();
}
The question is: is it safe to use the first example without jeopardize a website? Or should I use the session all the time for all the CRUD methods?
Thanks
Storing the context in the session is a terrible idea.
Read the following answer about it:
Entity Framework Object Context in ASP.NET Session object?
The context should be either created by method or by request.
To answer to your question:
Yes it safe to use the first approach and for sure more recommended then storing the context in a session.

javax.persistence.EntityNotFoundException: deleted entity passed to persist

I am using spring + JPA as orm framework. My project layer structure is like web --> Service --> Domain DAO --> genericDAO.
In genericDAO I am injecting EntityManager using #PersistenceContext.
genericDAO.delete(Object o) {
o = entityManager.merge(o);
entityManager.remove(o);
}
genericDAO.saveOrUpdate(Object o) {
entityManager.merge(o);
entityManager.flush();
}
In one method in service layer, I have following operations.
// delete order item if already exists.
Order order = getOrderFromSession();
if (CollectionUtils.isNotEmpty(orderItems)) {
Iterator<OrderItem> iterator = orderItems.iterator();
while (iterator.hasNext()) {
OrderItem orderItem = iterator.next();
iterator.remove();
orderDAO.deleteOrderItem(orderItem); // Which internall calls genericDAO.delete()
}
}
//orderDAO.saveOrder(order) // line Y
//Now create fresh order items submitted by jsp form.
for (ProductVO productVO : productList) {
if (productVO.getQuantity() > 0) {
orderItem = new OrderItem();
Product product = productDAO.getProductByCode(productVO.getCode()); // line X
orderItem.populateOrderItemByProduct(product, productVO.getQuantity(), order);
order.addOrderItem(orderItem);
}
}
Line X retrieve product entity using hql. But when line X is executed, I get below error.
javax.persistence.EntityNotFoundException: deleted entity passed to persist: [core.entity.OrderItem#].
I do not understand if order item is already marked as deleted in entity manager, why it tries to persist.
When I uncomment line Y, which internally flush the entity manager, it works fine. I do not understand why it requires entity manager to be flushed before executing line X
Here is a quote from hibernate documentation
Transactional persistent instances (i.e. objects loaded, saved,
created or queried by the Session) can be manipulated by the
application, and any changes to persistent state will be persisted
when the Session is flushed. There is no need to call a particular method (like update(), which has
a different purpose) to make your modifications persistent. The most
straightforward way to update the state of an object is to load() it
and then manipulate it directly while the Session is open.
Sometimes this programming model is
inefficient, as it requires in the same session both an SQL SELECT to
load an object and an SQL UPDATE to persist its updated state.
Hibernate offers an alternate approach by using detached instances.
But I'll try to explain simplier. Your method getOrderFromSession() is transactional and hibernate objects have session open inside it, but when object order is returned to you, it has been detached from session and hibernate doesn't know what you are doing with it, until you persist him again. So for deleted items hibernate will find out when you save that object, until then object in hibernate have same state as it was in a moment when getOrderFromSession() has return it.
Here you have detailed explanation
UPDATE:
When you delete object in hibernate, object in java becomes transient. It still exist in java and after delete you can use it.
Session.delete() will remove an object's state from the database. Your
application, however, can still hold a reference to a deleted object.
It is best to think of delete() as making a persistent instance,
transient.

JPA MERGE failed to update entity field value when this field is a collection(using ElementCollection)

Here we have a Manifest class that includes list of students and teachers, both could be null.
class Manifest{
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "MANIFEST_STUDENT")
List<String> students = new ArrayList<String>();
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "MANIFEST_TEACHER")
List<String> teachers = new ArrayList<String>();;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "MANIFEST_OTHERS")
List<String> others = new ArrayList<String>();;
}
on the UI, there are two multiple select, one for student and one for teacher that let user choose for current manifest.
here is the problem:
When user deselect all students or teachers from the list(meaning remove all students or teachers from current manifest) and click save, unfortunately nothing can be saved, from UI and database it shows that the multiselect chosen looks the SAME as before.
from service layer, the code is simply like this.
manifest.merge();
It seems we must keep at least one student or teacher for the collection field to make the change valid. So what's going on here and what is the solution? BTW, we are on Openjpa.
Kind of resolve the issue, more like a work around:
Before calling merge(), place several condition checkers to make sure the collection fields are not null
public void save(Manifest entity) {
if(entity.getStudents()==null){
entity.setStudents(new ArrayList<String>());
}
if(entity.getTeachers()==null){
entity.setTeachers(new ArrayList<String>());
}
if(entity.getOthers()==null){
entity.setOthers(new ArrayList<String>());
}
entity.merge();
}
Simple as it, it seems the UI returns those collection fields as null even we initiate them as with empty String lists.
cheers.
Initializing a value in a JPA managed class, such as class Manifest, has no bearing on what, or how, JPA will create the class as JPA maps extracted rows to the class. In particular, the result of:
List<String> students = new ArrayList<String>();
is likely to be:
On creation (by JPA) of a new instance, assign an ArrayList<String>() to students.
JPA overwrites students with the data it extracts - the empty ArrayList is dereferenced/lost.
If your code is clearing a list, such as students, use obj.getStudents().clear(). More likely to run into problems if you call obj.setStudents(someEmptyList).
The issue here is how the JPA manager handles empty datasets: as null or as an empty list. The JPA spec (old, not sure about the just released update) doesn't take a position on this point. A relevant article here.
From your comments, it's apparent that OpenJPA may not be respecting a null value for a Collection/List, while it happily manages the necessary changes for when the value is set to an empty list instead. Someone knowing more about OpenJPA than I may be able to help at this stage - meanwhile you've got a workaround.

Integration Test Strategy for Create methods

I want to test if created entity has been correctly persisted to database.There is a service integration test for create method:
#SpringApplicationContext({"setting ...."})
public class PersonServiceIntegrationTest extends UnitilsJUnit4 {
#SpringBeanByName
private PersonService personService;
#Test
public void createPerson() {
String name = "Name";
String sname = "Surename";
DtoPerson item = personService.createPerson(name, sname, Arrays.asList( new DtoAddress("Pisek","CZE", true), new DtoAddress("Strakonice", "CZE", false) );
Assert.notNull("Cannot be null", item);
/*
* This assertion fails because of transaction (I suppose) - item is not in
* database right now.
* Why? Returned dto 'item; is not null?
*/
//domain with all fetched related entities, eg. address
Person p = personService.getPerson(item.getIdPerson());
List<Address> addresses = p.getAddresses();
Assert.notNull("Cannot be null", p);
Assert.notNull("Cannot be null", addresses);//return collection of Address
Assert.notFalse("Cannot be emtpty", addresses.isEmpty());
ReflectionAssert.assertPropertyLeniens("City", Arrays.asList("Pisek", "Strakonice"), addresses);
}
}
Is it necessary to test create entity if I use hibernate? Someone can write you try to test low-level hibernate but hibernate has own tests. There is a trivial code above but I can imagine some specific code which persists more entites at same time (eg. one-many plus several one-one relations). And I want to test if relations has been correctly persisted.
Is there a pattern to do test this way? I have a problem, that record is not at database. I don't want to use returned dto (it presents only agregate root entity - person, but it does not say about person basic data (one-many), person address (one-many) etc.)... i want to get persisted record.
What I do to test the persistence is:
1) I create the Domain entity,
2) save it with Hibernate/JPA,
3) flush and clear the hibernate session/entity manager
4) load the entity again with hibernate
5) compare the original entity with the one that I have (re)loaded
so I am pretty sure that the mapping is more or less correct and every thing get persisted
I decided to rework service method for create person.
PersonService is responsible only to create domain entity Person - test will do only test the returned DtoPerson and its values.
PersonService will inject AddressService, PersonBasicDataService, which they have own create methods with collection as parameter. These services will have own test classes and test only returned collection of DtoAddress or DtoPersonBasicData.
Tests will be simply and will solve only own responsibility. :-)
As #Ralph said in comments under his answer - this test case is not about service layer. There is necessary to test domain layer. And what there is a new idea which I do not use in integration tests - tests has own hibernate session.

Resources