Working with JPA+Hibernate cache

Asked

Viewed 1,408 times

8

I doubt how I should work with the objects EntityManagerFactory and EntityManager.

Currently I urge a EntityManagerFactory for the whole system, since I have only one bank, I create only one and use it to create my own EntityManager. The EntityManager i usually instate one per screen, ie one for each crud. It is right this way to manage them?

Here comes my second question:

Here’s what’s going on:

  • User 1 searches the record x
  • User 2 makes a change in record x
  • User 1 updates the screen and the x record remains unchanged, as if User 2 had not interacted
  • And in the database is updated as User 2 changed
  • If I close the whole system of User 1 and reopen is there the record x updated
  • Why not update to User 1? I’ll always have to create a new one EntityManager for each interaction with the database?

    Method that searches the data:

    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;
    }
    

    Section 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();
        }
    
    • Possible duplicate (or not): http://answall.com/questions/45758/bestforma-de-utilizar-e-instancer-o-entitymanagerfactory

    • What exactly are your screens? Swing components? JSP pages? Servlets? HTML forms with ajax? A lot of System.out.println?

    3 answers

    6


    Some methods of acquiring entities rescue the entity from memory if it is already there, and not from the database. For example, you seek 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 will occur is that you will not get the updated person from the database but rather the same instance you had already obtained on the first call from find (the instance will be rescued from memory and no new one will be created from the database).

    If you then want to provide say a button for the user to refresh the information he has on the screen, you do not need a new instance of Entitymanager - you can actively request that the instance be updated with the database information, thus:

    entityManager.refresh(pessoa);
    

    If the scope of your Entitymanager is really reduced and clear, you can be even bolder and invoke the method clear:

    entityManager.clear();
    

    This will completely clear the context (this instance of Entitymanager) so that all following requests will be taken to the database. Of course, this command will also untie all entities in this context and any changes in these entities will be lost (unless you attack them again).

    • I did a test like this: I created the manager = factory.createEntityManager();, I cleaned up manager.clear(), I made the namedQuery and closed it manager.close(). Then I changed a record in the bank, I re-ran the test again and it still brings out-of-date information. There is no way that other systems have this same problem. And worst, I followed the show_sql and he makes the queries, just doesn’t update the objects.

    • @Gleison Then you will have to show your code that changes the record and the code that expects to read the updated record.

    • put in the question the excerpt that does the research and click on Jtable, how much the change in the bank I do in hand, because the idea is to simulate that someone else changes the record, not necessarily who is researching.

    • @Gleison And when you change your hand in the database, do you commit the transaction before trying to see the updated records on the screen? See: if you use Oracle, for example, the default is to open a transaction when you make the first change, then the transaction is open until you close.

    • This is not the case, I use Mysql and do without transaction even. So when I close the system and open again the data is there updated. It seems that the manager.clear() does not clear the cache as Victor mentioned.

    1

    Scope of EntityManager

    Answering the first question: maintain an instance of EntityManager by class does not seem correct.

    Of course this can work in certain circumstances, as in the case of the class being recreated with each request in a web system, so there will be a EntityManager new to each thread and there will be no competition problems.

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

    1. Get a new EntityManager in each method accessing the database. In most cases, creating a EntityManager is fast, in contrast to the creation of the EntityManagerFactory, which should only be done once DataSource.
    2. 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 the complexities of the management and at the same time allow more flexibility in the management of objects, I would recommend using some Dependency Injection framework, such as Spring, which allows the general configuration of the EntityManagerFactory in a single location and the automatic distribution of EntityManager where necessary. A great advantage is that you can change the scope of the EntityManager as you wish 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 dismembered into two parts:

    Competition

    Users act simultaneously and generate concurrent requests that run at the same time on the server.

    In this case, care should be taken to define ACID transactional blocks whenever there are multiple access to databases within a single request, in order to ensure the integrity and consistency of the data in the database.

    Also, 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.

    The ideal is that there is no kind of sharing to ensure a complete parallelism without the need for synchronizations.

    Stale data (outdated data)

    This is the case described in the second question. It consists of a user trying to act on data in a state prior to that existing in the remote system.

    There are some strategies to avoid this type of problem, the most common and effective (although laborious) versioning of records in the database.

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

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

    IdCliente   NomeCliente   VersaoRegistro
    11          Luiz          1
    

    Now the user To 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 performs a name change. When the system receives the update request, it checks the version sent by B, that 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 the user To try to change the record, the system will check that version that To is trying to change is earlier than the current version. The system then blocks the change and responds with some message like: data has been changed since your last query, please try again.

    Treatment may vary. Of course, the sooner the user is notified the better, so he doesn’t lose the job.

    • Great answer, although the question is old. The only detail I will pay attention to is this paragraph that got strange: "Current users at the same time and generate concurrent requests that run at the same time on the server."

    • @Victorstafusa Thank you so much! It’s fixed...

    1

    You must have a EntityManager by database interaction. This means:

    • Never compatilhe EntityManagers between different threads. They are not designed to be single-thread.
    • If you reuse threads to handle different requests or interactions, remember not to reuse EntityManagers.

    This behavior must be the result of isolation levels that the database provides, that may be:

    • Read uncommited - Allows a transaction to view non-committed data from another transaction (which is called Dirty read).
    • Read commited - Allows read only commited data until the moment the reading is made. But two identical readings in the same transaction at different times can bring different results to the same tuples (which is called non-repeatable read).
    • Repeatable reads - Rereading of 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 brings more results than the first (which is called phantom read).
    • Serializable - The transaction only sees the data that existed until the moment it was started the way it was when it was started.

    The behavior you saw between users 1 and 2 demonstrates that Hibernate should be using the levels repeatable reads or serializable.

    To handle this, you should check that the scope of your transactions is neither larger nor smaller than what they should be, which usually (but not always) means encompassing the entire request, just like that (or anything that is similar to a request if you are on a desktop or batch system).

    You can use the method EntityManager.clear() to clear any cache Hibernate makes when initiating or terminating a transaction. Also, at the end of the transaction you can use EntityManager.close() to complete it and make sure you do not attempt to use the EntityManager closed again after this (provided that the container or the dependency injection mechanism no longer does this automatically).

    Related: /a/45784/132

    • Definitely and absolutely not what this is about.

    • @Caffé I don’t really know, I reread the question and as there is no code I was in doubt. However this excerpt makes me suspect that the main problem is that OP is using Entitymanager incorrectly: The Entitymanager I usually instate one per screen, ie one for each crud. It is right this way to manage them? - And that’s mainly what led me to produce this answer.

    • There is no problem in instantiating "an Entitymanager per screen". Or there?

    • @Caffè. Depends, what this "screen" is? If it is a Servlet for example and you instantiate one by Servlet and share among all requests, you’ve seen what you see, right?

    • Why Servlet can work well too. It depends on the scope of Servlet.

    • @Caffe. That’s right, depends on. That is to say, it’s not clear what you’re asking. - http://answall.com/questions/47078/como-trabalhar-com-o-cache-do-jpahibernate/47083?noredirect=1#comment94802_47078

    Show 1 more comment

    Browser other questions tagged

    You are not signed in. Login or sign up in order to post.