0

I’ve been working on a website and want to ask why the newly created records that I save to a database via Spring JPA don't appear in other queries within the server. The records themselves are saved just fine and do appear in the database itself but not when the server looks for them. Older records that were not made by the server do appear just fine.

For example, a new book is added with the ID of 601 and it simply will not appear in the results of a any API that is run on the server. Even when it is searched for specifically, the server returns a 404 Error.

I've consulted a number of articles on the matter including Spring's own documentation but I haven't been able to find an answer on my own. I'll go into the details of my attempts below.

Articles and Attempts

While I've consulted a large amount of material on the matter, I'll trim it down to only a few that I think are most relevant.

The the most promising suggestion that I received was to force a refresh of the Entity's cache and the most cited way to do this was with Entity Manager's em.refresh(); method. Judging by the documentation, it does sound like it should do the job quite well. Sadly, this is not a method available in standard JpaRepository libraries since JpaRepository is an abstraction over Entity Manager so I've been working to implement Entity Manager into my code.

The primary source I used was here sicne it provided a way to implement a refresh ability in any repository that needed it and I followed the code given in the answer very closely but it had no effect. It was a four year old answer so it might be possible that it's out of date. I can't quite tell.

I've tried a few different ideas but this one has had the most promise.

Code:

Entity

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    int bookID;
    
    @NotEmpty
    String title;
    String isbn;
    
    Integer series_part;
    Integer edition;
    int format;
    int pages;
    int authorID;
    int publisherID;
    int seriesID;
    int genreID;
    int languageID;
    int copyright;

Service Implementation

@Service
public class BookServiceImpl implements BookService{
    
    @Autowired BookRepository bookRepository;
    
    @Override
    public Book createBook(Book book) {
        Book savedBook=bookRepository.save(book);
        bookRepository.refresh(savedBook);
        return savedBook;
    }
}

Service Controller

@RestController
@RequestMapping(path = "/books")
@CrossOrigin(origins = "http://localhost:8080")
public class BookServiceController {
    
    @Autowired BookService bookServiceImpl;
    
    @PostMapping(path = "/create")
    public ResponseEntity<Book> createBook(@RequestBody Book book) {
        try {
            Book newBook = bookServiceImpl.createBook(book);
            return new ResponseEntity<>(newBook, HttpStatus.CREATED);
        } catch (Exception e) {
            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

Get APIs for Book

@GetMapping(path = "/all")
    @Transactional(readOnly = false)
    public ResponseEntity<Object> getBooks() {
        return new ResponseEntity<>(bookServicesImpl.getBooks(), HttpStatus.OK);
    }

    @GetMapping(path = "/ten")
    public ResponseEntity<Object> getTenBooks() {
        return new ResponseEntity<>(bookServicesImpl.getTenBooks(), HttpStatus.OK);
    }

    @GetMapping(path = "/specific/{id}")
    public ResponseEntity<Object> getSpecificBook(@PathVariable("id") int id) {
        return new ResponseEntity<>(bookServicesImpl.getSpecificBook(id), HttpStatus.OK);
    }

    @GetMapping(path = "/search/{term}")
    @Transactional(readOnly = false)
    public ResponseEntity<Object> searchForBook(@PathVariable("term") String term) {
        return new ResponseEntity<>(bookServicesImpl.searchForBook(term), HttpStatus.OK);
    }

Generic Repository that contains refresh method

I copied it quite faithfully (possibly too faithfully).

public class ElementRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
    implements ElementRepository<T, ID> {

  private final EntityManager entityManager;

  public ElementRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
    super(entityInformation, entityManager);
    this.entityManager = entityManager;
  }

  @Override
  @Transactional
  public void refresh(T t) {
    entityManager.refresh(t);
  }

These seemed like the most relevant classes to include but I'll include any others that might be needed.

I apologize for the frequency of questions but I fully admit to being a novice of JPA and a lot of this (including large sections of the documentation) goes over my head from time to time.

Thanks in advance for any help at all!

TeaDrinker
  • 199
  • 1
  • 11
  • Could you share some exaplme of an API that fetches the newly created book? – Szarpul Jul 06 '22 at 09:34
  • 2
    Your service isn't transactional.. No transaction, no persistence. – M. Deinum Jul 06 '22 at 12:25
  • @Szarpul I added the examples you asked about. I'm not sure why I forgot them in the first place. That being said, I think the issue is fixed. The records are showing up like they should be. It's the worst kind of fix though since I have no idea what changed to make it start working properly. I need to do some testing with this and see what happened. – TeaDrinker Jul 06 '22 at 12:25
  • @M.Deinum I added a `@Transactional` tag to the service but it had no effect. I added a few configurations as well to experiment but they did nothing either. – TeaDrinker Jul 06 '22 at 12:41
  • My earlier comment about it working was premature. The server suddenly started reading one of the test records I added through the website but as soon as I added another one, that new record wasn't being read. – TeaDrinker Jul 06 '22 at 12:42
  • @M.Deinum I got it working again but to ensure that the problem truly is gone, I'll need a lot more time to test the fix than I anticipated. I'll have a proper response on this later. I think the `@Transactional` annotation did have something more to do with it than my first comment let on but I also don't think it was the whole problem. – TeaDrinker Jul 06 '22 at 12:52
  • Also why the refresh? That doesn't really do anything then return the already present object. So a `refresh` is going to solve nothing. Point is trying to persist something into the database without using a transaction will not occur you simply need a transaction. – M. Deinum Jul 06 '22 at 16:38
  • @M.Deinum I was working with refresh because I misunderstood how JPA worked. I cannot emphasize enough that there's a lot to this library that I simply don't understand yet. I've learned that it wasn't needed and I've removed all references to it and Entity Manager in the application. – TeaDrinker Jul 06 '22 at 23:57
  • @M.Deinum That being said, I figured out the issue(s). The first problem was the lack of `@Transactional` annotations and the second was an inability to properly fill the needed fields in the database. For the purposes of testing, I had been using a simplified and stripped down POST statement that only filled in a handful of the field in a typical instance of the entity. I was only filling in the fields that couldn't be Null in the database. This wasn't a problem when creating the records but for reasons that I can't precisely say, they simply wouldn't return when called. That has been fixed. – TeaDrinker Jul 07 '22 at 00:02
  • @M.Deinum One final comment. Everything is fixed and working as intended now and I've since implemented several other POST statements in the website at large. They're working too. – TeaDrinker Jul 07 '22 at 00:04
  • I don't think you need `@Transactional` nither a `refresh` method. CRUD operations on repository are transactional by default. – Szarpul Jul 07 '22 at 08:49

0 Answers0