Pergunta

I'm developing a Spring based server. I have two Controllers that are listening to different http requests. Both of them have a variable referencing the same Repository with the @Autowired annotation.

@Controller
public class ClientController {
    @Autowired
    NodeRepository nodeRepository;
    ...
}

@Controller
public class NodeController {
    @Autowired
    NodeRepository nodeRepository;
    ...
}

All the database operations are done by using the NodeRepository:

public interface NodeRepository extends JpaRepository<Node, Long>{
    Node findByNodeId(long nodeId);
    List<Node> findByStatusIn(Set<String> status);
    List<Node> findByNodeIdIn(Set<Long> ids);
    Node findByPhoneNumber(String phoneNumber);
    Node findByCloudId(String cloudId);
    Node findByDeviceDescription(String deviceDescription);
}

The behaviour is the following:

When the ClientController receives a new request, it is processed and the Controller sends a request to an external device and waits for its answer.
This answer is received by the NodeController. So when the answer arrives, the information received is persisted in the database and a signal is sent to the ClientController to wake it up.

My problem comes when this controller is waken up and tries to retrieve the updated information from the database. The information is NOT the one that has just been stored but it is the one that was stored previously.

Here is the snippet of code of the ClientController where the block and retrieval information happens:

// Retrieves the list of nodes that are going to be requested
requestedNodes = (List<Node>) nodeRepository.findAll();

System.out.println("BEFORE");
for (Node node : nodes)
    System.out.println(node.getNodeId() + ": " + node.getStatus());

// Sends a request to the nodes
// The Controller is blocked here until all answers have been received
fetchNodes(requestedNodes, DevicePing[0], null);

// Retrieves the list of nodes again
nodes = nodeRepository.findAll();

System.out.println("AFTER");
for (Node node : nodes)
    System.out.println(node.getNodeId() + ": " + node.getStatus());

And here is the printed data:

BEFORE:
321: dead
1: dead
4: dead

AFTER:
321: dead
4: dead
1: dead

As you can see the status is the same, but the second time it should be 'alive'.

I've checked if the data it is being stored properly, and it is. Before unlocking the ClientController, the new data is available in the database.

I've also tried retrieving the new data using JDBC, intead of the Repository. In this case, the new information is correctly retrieved. So I think that it must be something related to the Repository but I couldn't find what it is. Maybe there is some cache or flush issue. I tried to execute nodeRepository.flush() right after saving the new data and right before retrieving it, but it didn't work.

I hope someone could help me. Thanks in advance

Foi útil?

Solução

Finally I could find the answer. The problem was indeed related to some caching issues.

Hibernate was caching the Entities retrieved with the function nodeRepository.findAll(), so when I was calling the function nodeRepository.findAll() it was returning the cached information instead of performing a query to the database.

The solution is to call the function clear() from the EntityManager right before retrieving the updated data:

// Sends a request to the nodes
// The Controller is blocked here until all answers have been received
fetchNodes(requestedNodes, DevicePing[0], null);

// Clears the cache to avoid inconsistency
entityManager.clear();

// Retrieves the list of nodes again
nodes = nodeRepository.findAll();

Outras dicas

EntityManager entityManager;
@Autowired
public Service(final EntityManager entityManager) {
    this.entityManager = entityManager;
}
entityManager.clear();

This works for me, thanks

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top