0

I have a situation where I want to retrieve data, but before it is returned I want to change it without saving/persisting.

Here is my rest controller:

@RestController
@RequestMapping("api/drawing-releases")
@CrossOrigin("*")
public class DrawingReleaseRestController {
    @Autowired
    private DrawingReleaseService service;

    @RequestMapping(method = RequestMethod.GET, value = "/{id}")
    @JsonView(View.DrawingReleaseView.class)
    public ResponseEntity<DrawingRelease> getWithId(@PathVariable int id) throws EntityNotFoundException {
      return new ResponseEntity<>(service.getByID(id),HttpStatus.OK);
    }
}

Here is the service implementation:

@Service
public class DrawingReleaseServiceImpl extends DrawingFunctions implements DrawingReleaseService {
    /**
     * Logging Manager
     */
    private static final Logger LOGGER=LogManager.getLogger();

    @Autowired
    private DrawingReleaseRepository repository;

    @Override
    public DrawingRelease getByID(int id) throws EntityNotFoundException {
        Optional<DrawingRelease> opt = repository.findById(id);
        ...
        // manipulate the data here
        ...
        return opt.get();
    }
}

Intitially I had the @Transactional annotation on the service. By removing that and not having it on the getByID method, I was first thinking that the session would open and close with the repository as indicated by the answer here.

That did not work, and I saw in the comments that "the session lasts for the entire duration of HTTP request processing." So, in the service I added

@Autowired
private EntityManager em;

and then in the getByID method I added

em.close();

before I made any changes. However, any changes I make are still being persisted.

Is there any way that I can make unsaved changes in my service layer? I suppose I could create some POJOs that mirror the entities (but aren't entities) and then copy the data and return those objects, but it doesn't seem like I should have to do something like that.

Tim
  • 1,605
  • 6
  • 31
  • 47

2 Answers2

2

You say you want to get an Entity and then change the data and then return it without the entity being persisted but IMHO you shouldn't be using the entity for this. Why are you changing the data and what is the user going to do with it since it no longer represents what's in the database. What other changes are going to be made or what other assumptions (this is a database object) are going to be invalid?

If you are using a database object to derive a different object you should have a different class for this purpose. This is common and is called a Data Transfer Object (DTO) and they can be created from spring-data-jpa directly using projection. Your Dto object would take an entity as a constructor and fill out whatever properties it wants to from the entity. The Dto is not a persistent object so there are no issues around it being saved.

Much clearer and more understandable design and consistent with SOLID Principles and code structure.

K.Nicholas
  • 10,956
  • 4
  • 46
  • 66
  • Maybe I should look into that more. As to why I want to do this--I have a drawing which can have a parts list. Any drawing can be used on a parts list for another drawing. On its own, a drawing can have parts 1, 2, and 3. If that drawing is part of another parts list, say at line 12, then I want it to show as 12, 12-1, 12-2, and 12-3. On another drawing, it might be on the parts list at line 15, so 15-1, 15-2, etc. I still want it to be saved in the database as items 1, 2, and 3, but shown as something else depending on the context. – Tim Oct 31 '18 at 17:15
  • Right, exactly. Separate your Problem Domain from your Persistence Domain. You don't want to modify information in the part because of how the drawing is using it, especially since every drawing would be using it differently. Check out some design patterns around this concept. I don't want to run off and start suggesting but definitely don't change the entity like that for that reason. – K.Nicholas Oct 31 '18 at 17:17
1

Yes, you can detach the entity from the persistence context: https://docs.oracle.com/javaee/7/api/javax/persistence/EntityManager.html#detach-java.lang.Object-

Bernie
  • 2,253
  • 1
  • 16
  • 18
  • I `detach`ed the top level object, but it still persisted the lower level object. If I `detach` the lower level object, then I get a PersistentObjectException: detached entity passed to persist. Any ideas on that? – Tim Oct 31 '18 at 14:52
  • It looks like I'm getting the detached entity exception if I make a call to any repository, even if the repository is unrelated to the detached entity. Is that to be expected, and is there a way around that? – Tim Oct 31 '18 at 16:04
  • spring-data-jpa uses separate entity-managers for each repository and it manages the connection on its own so trying to get complicated with JPA *and* using spring-data-jpa is going to be an exercise in frustration. You should decide whether you want to use JPA or spring-data-jpa and focus your thinking around one or the other. – K.Nicholas Oct 31 '18 at 16:58
  • @K.Nicholas I'm not really following you. What would be the way to focus on one of them? – Tim Oct 31 '18 at 17:06
  • By top level and lower level I assume you mean parent and child? Or inheritance? It's not clear what your issue is since you are assuming we know your code. In short, spring-data-jpa is NOT jpa. It's an abstraction layer on top of jpa. I'm saying you need to understand that first before attempting to use spring-data-jpa incorrectly. Secondly, and I think more importantly, see answer below. – K.Nicholas Oct 31 '18 at 17:10