Incorrect JPA entity column type causing issues for FindById - spring

I'm currently working on a Spring backend application. We have a Partner entity that has the #GeneratedValue(strategy = GenerationType.IDENTITY annotation for autogenerating primary keys. The primary key partnerId is of type long.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "partner_id")
private long partnerId;
In our Partner controller class, we wrote some functions for basic CRUD operations. When we make a post call to add a Partner, we omit the primary key partnerId and include information for the other fields. The post is successful and the data can be seen in the database.
However, when we try to make a put call to update the newly added Partner, it instead creates a new Partner entry in the Partner table.
At first, it seemed like a code level issue, but we tested the put call on one of the entries that we added to the table from our base data set, it works fine. We used FindById() and isPresent() to see if an entry exists for this new user with partnerId from the incoming Partner object from the put call, but got false. But with one of the 3 existing Partner entries that we put into the database manually with queries, they returned true.
#RequestMapping(method = RequestMethod.PUT, value = PATH)
public ResponseEntity<?> updatePartner(#RequestBody Partner partner) {
...
System.out.println(partnerRepository.findById(partner.getPartnerId()).isPresent());
}
It seems as though the post call is setting the new Partner object in the database, but somehow the partnerId of any newly posted entry is not of type long, which disrupts findById().isPresent(). I'm just wondering if anyone knows why this is happening since our entity sets the primary key to type long and the controller is sending to the database a Partner object.

Related

How to retrieve an Entity Object just with #Id (Proxy object), check if it exists and assign it to a #ManyToOne association

I have an entity Product which have many fields and associations (around 60).
And a table ProductView which has a #ManyToOne(fetch = FetchType.LAZY) association with Product.
Is there a optimal way to retrieve Product object and assign it to ProductView ?
If its used JPA findById(productId) or JPQL/EntityManager selects-> It will retrieve all products fields and associations
Product product = productRepository.findById(productId);
ProductView productView = new ProductView(product);
save(productView);
If its used JPA getOne -> It solves the problem but the Proxy can throw error if Product does not exists. And this error can not be handled because it happens at runtime.
Product product = productRepository.getOne(productId);
ProductView productView = new ProductView(product);
save(productView);
If a DTO is used or Interface which refers to the same Product Table -> We will get just an object with Id field, but a lot more processes will need to be added (Which I am not familiar with)
Delete foreign keys from ProductView table (#ManyToOne -> #Column) and simple assign productIds. But in this way, there will be no logic connection between tables.
ProductView DB
How usually developers avoid this problem ?
I don't understand what the problem is. Just use getOne approach and at the end of your method, use flush which will throw the constraint violation exception that you can handle. This is the way to go.

Spring #Transactional is not commited. Neo4J

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.

Updating property and resource link in single PUT query with Spring Data Rest

OK so let's start self referencing object, something like this:
#Data
#Entity
public class FamilyNode {
#Id
#GeneratedValue
private long id;
private boolean orphan;
#ManyToOne
private FamilyNode parent;
}
And a standard repository rest resource like this:
#RepositoryRestResource(collectionResourceRel = "familynodes", path = "familynodes")
public interface FamilyNodeRepository extends CrudRepository<FamilyNode, Long> {
}
Now, let's assume some parent objects which I want to link with already exist with ID=1 and ID=2, each of which were created with a POST to /api/familynodes which looked like this:
{
"orphan": true,
}
If I attempt to create a new client (ID=3) with something like this using a POST request to /api/familynodes, it will work fine with the linked resource updating fine in the DB:
{
"orphan": false,
"parent": "/api/familynodes/1"
}
However, if I attempt to do a PUT with the following body to /api/familynodes/3, the parent property seems to silently do nothing and the database is not updated to reflect the new association:
{
"orphan": false,
"parent": "/api/familynodes/2"
}
Similarly (and this is the use case that I'm getting at), a PUT like this will only update the orphan property but will leave the parent untouched:
{
"orphan": true,
"parent": null
}
So you now have a record which claims to be an orphan, but still has a parent. Of course you could do subsequent REST requests to the resource URI directly but I'm trying to make rest operations atomic so that it's impossible for any single rest query to create invalid state. So now I'm struggling with how do that with what seems like a simple use case without getting into writing my own controller to handle it - am I missing a mechanism here within the realm of spring data rest?
This is the expected behaviour for PUT requests in Spring Data Rest 2.5.7 and above wherein a PUT request does not update the resource links, only the main attributes.
As detailed here by Oliver Gierke:
If we consider URIs for association fields in the payload to update those associations, the question comes up about what's supposed to happen if no URI is specified. With the current behavior, linked associations are simply not a part of the payload as they only reside in the _links block. We have two options in this scenario: wiping the associations that are not handed, which breaks the "PUT what you GET" approach. Only wiping the ones that are supplied using null would sort of blur the "you PUT the entire state of the resource".
You may use a PATCH instead of PUT to achieve the desired result in your case

Incorrect derived query for byId in Spring Data Neo4j

I have two entities: User and Connection, along with two appropriate repositories. Both entities has #GraphId id field. Connection entity has User user field.
In ConnectionRepository interface I added following method:
List<Connection> findByUserId(long userId)
But it doesn't work. It generates incorrect cypher query. I think it incorrect, because it contains clause like this:
WHERE user.id = 15
which is not working, because id is not a property. It must be:
WHERE id(user) = 15
Is this a bug? In any case, how can I get it to work?
The derived query translates to the property id of the user defined on the Connection. It is quite possible that node entities contain a user managed id property as well and it would be incorrect to assume that id is always the node id.
In this case, you might want to use a #Query instead.
#Query("MATCH (user:label) WHERE ID(user)={0} return user")
List<Connection> findByUserId(long userId)

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.

Resources