Working with the JPA + Hibernate cache

8

I have questions about how to work with objects EntityManagerFactory and EntityManager .

Currently I instantiate a EntityManagerFactory for the whole system, as I only have one bank, I create only one and use it to create my EntityManager . The EntityManager I usually instantiate one per screen, or one for each crud. Is this certain way to manage them?

Here comes my second question :

The following is happening:

  • User 1 searches the registry x
  • User 2 makes a change to the registry x
  • User 1 updates the screen and the registry x remains unchanged, as if User 2 had not interacted
  • E in the database is updated as User 2 has changed
  • If I close the entire system of User 1 and reopen there is the updated x-register

Why do not you upgrade to User 1? Will I always have to create a new EntityManager for each database interaction?

Data search method:

public List<Evento> getListPeriodo(Date inicio, Date fim, String texto) {
    Query query = getDbManager().createNamedQuery("Evento.getListPeriodo");

    query.setParameter("inicio", inicio);
    query.setParameter("fim", fim);
    query.setParameter("texto", "%" + texto + "%");

    List<Evento> lista = query.getResultList();
    return lista;
}

Excerpt where I populate a JTable with the search result:

    EntityManager dbManager = Constante.DBFACTORYLOCAL.createEntityManager();
    dbManager.clear();
    EventoDAO EventoDAO = new EventoDAO(dbManager);
    try {
        List<Evento> oe = EventoDAO.getListPeriodo(di, de, jTextFieldFiltro.getText());

        for (Evento t : oe) {
            String cntr = "";
            for (Item o : t.getItens()) {
                if (cntr.isEmpty()) {
                    cntr = o.getPalavra();
                } else {
                    cntr = cntr + ", " + o.getPalavra();
                }
            }

            Object[] linha = new Object[]{t.getEventoId(), t.getDescricao(), cntr, DateFunction.DateUtilToTexto(t.getDataInicio())};
            modelo.addRow(linha);
        }
    } finally {
        dbManager.close();
    }
    
asked by anonymous 15.01.2015 / 21:14

3 answers

6

Some methods of retrieving entities retrieve the entity from memory if it is already there, not the database. For example, you search for a person:

pessoa = entityManager.find(Pessoa.Class, id);

Then someone changes and persists this same person and you search again using the same method above and the same instance of EntityManager . What is going to happen is that you will not get the updated person from the database but the same instance that you had already obtained on the first find call (the instance will be rescued from memory and will not be created a new one from the bank).

If you want to make a button let the user refresh the information they have on the screen, you do not need a new EntityManager instance - you can actively request that the instance be updated with the database information, like this:

entityManager.refresh(pessoa);

If the scope of your EntityManager is really small and clear, you can be even more adventurous and invoke the clear method:

entityManager.clear();

This will completely clear the context (this instance of EntityManager) so that all subsequent requests will be taken to the database. Of course, this command will also unpin all entities in this context and any changes to those entities will be lost (unless you re-attach them).

    
15.01.2015 / 21:49
1

You must have EntityManager per database interaction. This means:

  • Never share EntityManager s between different threads. They are not designed to be single-threaded.
  • If you reuse threads to handle different requests or interactions, remember not to reuse% s of% s.

This behavior should be a result of isolation levels that the database offers , which can be :

  • Read uncommited - Allows a transaction to see uncommitted data from another transaction (called dirty read).
  • Read commited - Allows you to read only committed data up to the time the read is taken. But two identical readings in the same transaction at different times may bring different results for the same tuples (what is called non-repeatable read).
  • Repeatable reads - Rereading any data already read will produce the same result as the previous readings in the transaction. However, new data entered by other transactions can still cause two identical readings to produce distinct results, where the second results in more than the first (which is called phantom read ).
  • Serializable - The transaction only sees the data that existed until it was started the way it was when it was started.

The behavior you have seen between users 1 and 2 demonstrates that hibernate should be using the repeatable reads

15.01.2015 / 21:49
1

Scope of EntityManager

Answering the first question: keeping an instance of EntityManager per class does not seem right.

Of course, this may work under certain circumstances, such as if the class is re-created for every request on a web system, so that there will be a EntityManager new for each thread and will not incur competition issues.

In a web-based system with concurrent access, there are two simple approaches that are common:

  • Get a new EntityManager on each method that accesses the database. In most cases, the creation of EntityManager is fast, in exchange for the creation of EntityManagerFactory , which should only be done once per DataSource .
  • Get a new EntityManager for thread or request. In more "heavy" routines this is usually better for performance, but presents some management challenges. Manual implementation can be done using ThreadLocal .
  • To avoid spending time on management complexities while allowing more flexibility in managing objects, I would recommend using some Dependency Injection framework, such as Spring, which allows the overall configuration of EntityManagerFactory in a single place and the automatic distribution of instances of EntityManager where necessary. One great advantage is that you can change the scope of EntityManager the way you want with virtually no work using annotations or framework settings.

    Versioning of records

    The scenario where two users access the same screen at the same time can be broken down into two parts:

    Competition

    Users act concurrently and generate concurrent requests that run concurrently on the server.

    In this case, it is necessary to consider defining ACID transactional blocks whenever there are multiple accesses to the databases within a same request, in order to guarantee the integrity and consistency of the data in the database.

    In addition, as I discussed in the previous topic, you need to look at any objects that might be improperly shared between the two requests, such as EntityManager .

    Ideally, there should be no sharing to ensure complete parallelism without the need for synchronization.

    Stale data (lagged data)

    This is the case described in the second question. It consists of a user attempting to act on data in a state earlier than the one on the remote system.

    There are a few strategies to avoid this kind of problem, and it is the most common and effective (though cumbersome) versioning of records in the database.

    This means that the action of a user that affects system state will only be valid if it proves that it has the current data.

    Think of a field of versao added in the table. For example:

    IdCliente   NomeCliente   VersaoRegistro
    11          Luiz          1
    

    Now the user A opens the screen with the above record being displayed. The value of VersaoRegistro is stored hidden on the screen.

    In the meantime, the user B accesses the same record and makes a change to the name. When the system receives the update request, it checks the version sent by B , which is 1 , updates the other fields and increments the version. Now we have the table like this:

    IdCliente   NomeCliente   VersaoRegistro
    11          Luiz S.       2
    

    When user A attempts to change the registry, the system will verify that the version A it is trying to change is earlier than the current version. The system then blocks the change and responds with some message like: the data has changed since your last query, please try again.

    Treatment may vary. Of course the sooner the user gets notified the better, so he does not lose his job.

        
    23.10.2015 / 08:12