title: Recently updated db records not reflected in follow-on SELECT
body: <p>I have a spring mvc app with an h2 database. Db objects are updated using the JPA criteria and spring @Transaction annotation. See findBySymbol() and update() at bottom.</p>
  <p>After update() is done, the app calls findBySymbol() to run some logic on the updated records. The findBySymbol() SQL is correct. The problem is that results from findBySymbol() have <em>not</em> been updated. This is puzzling because Hibernate trace logs show the update binded parameters, the SQL update statement, and the int result of executeUpdate(). Also confusing is that I can query the db at that point directly and see that the records are, in fact, updated.</p>
  <p>What do I need to do to be able to have the app query the db immediately after the update and return updated data?</p>
  <pre><code>import com.example.mycompany.beans.AEarnings;
  import java.math.BigDecimal;
  import java.time.LocalDate;
  import java.util.ArrayList;
  import java.util.List;
  import javax.persistence.EntityManager;
  import javax.persistence.PersistenceContext;
  import javax.persistence.TypedQuery;
  import org.springframework.stereotype.Repository;
  import javax.persistence.criteria.CriteriaBuilder;
  import javax.persistence.criteria.CriteriaQuery;
  import javax.persistence.criteria.Root;
  import javax.persistence.criteria.ParameterExpression;
  import javax.persistence.criteria.Path;
  import javax.persistence.criteria.Predicate;
  import javax.persistence.criteria.CriteriaUpdate;
  import javax.persistence.NoResultException;
  import org.springframework.transaction.annotation.Transactional;
  
  @Repository
  public class AnnualEarningsDaoImpl implements AnnualEarningsDao {
  
      @PersistenceContext
      EntityManager entityManager;        
  
      @Override
      public List&lt;AEarnings&gt; findBySymbol(String s) {
  
          // CriteriaBuilder 
          CriteriaBuilder cb = entityManager.getCriteriaBuilder();
  
          // CriteriaQuery
          CriteriaQuery&lt;AEarnings&gt; cQuery = cb.createQuery(AEarnings.class);
  
          // Root
          Root&lt;AEarnings&gt; ae = cQuery.from(AEarnings.class);
  
          // Parameters
          ParameterExpression&lt;String&gt; p = cb.parameter(String.class);
  
          // Db command
          cQuery.select(ae).where(cb.equal(ae.get(&quot;symbol&quot;), p));
  
          // TypedQuery
          TypedQuery&lt;AEarnings&gt; query = entityManager.createQuery(cQuery);
  
          // Set Parameter
          query.setParameter(p, s);
  
          // Results
          List&lt;AEarnings&gt; results = query.getResultList();
          
          // Return
          return results;
      }
      
      @Override
      @Transactional
      public void update(
              String a, LocalDate b, String c,
              BigDecimal d, BigDecimal e, BigDecimal f) {
          
          // CriteriaBuilder
          CriteriaBuilder builder = entityManager.getCriteriaBuilder();
  
          // CriteriaUpdate&lt;AEarnings&gt;
          CriteriaUpdate&lt;AEarnings&gt; update
                  = builder.createCriteriaUpdate(AEarnings.class);
          // Root
          Root&lt;AEarnings&gt; root = update.from(AEarnings.class);
  
          // Holds parameters
          List&lt;Predicate&gt; predicates = new ArrayList&lt;&gt;();
  
          // Predicates
          predicates.add(builder.equal(root.get(&quot;a&quot;), a));
          predicates.add(builder.equal(root.get(&quot;b&quot;), b));
          predicates.add(builder.equal(root.get(&quot;c&quot;), c));
          
          // Update
          update
              .set(root.get(&quot;d&quot;), d)
              .set(root.get(&quot;e&quot;), e)
              .set(root.get(&quot;f&quot;), f)
              .where(predicates.toArray(new Predicate[]{}));
  
          // Execute
          int i = entityManager.createQuery(update).executeUpdate();
          System.out.println(&quot;Updated: &quot; + i);
      }
  
  }
  </code></pre>
  <p>Updated with AEarningsServiceImpl, which in this case only calls dao methods:</p>
  <pre><code>import com.example.mycompany.beans.AEarnings;
  import com.example.mycompany.dao.AEarningsDao;
  import java.math.BigDecimal;
  import java.time.LocalDate;
  import java.util.List;
  import org.springframework.stereotype.Service;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.transaction.annotation.Transactional;
  
  @Service
  public class AEarningsServiceImpl implements AEarningsService{
  
      @Autowired
      AEarningsDao aEarningsDao;
  
      @Override
      public List&lt;AnnualEarnings&gt; findBySymbol(String sym) {
          return annualEarningsDao.findBySymbol(sym);
      }
  
      @Override
      @Transactional
      public void update(
              String symbol, LocalDate fiscalEndDate, String fiscalPeriod, 
              BigDecimal prev, BigDecimal diff, BigDecimal diffPercent) {
          annualEarningsDao.update(symbol, fiscalEndDate, fiscalPeriod,
                  prev, diff, diffPercent);
      }
  
      // save()...    
      // delete()...
  }
  
  </code></pre>
  <p>Updated with pom.xml:</p>
  <pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
  &lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; 
           xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
           xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 
              https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;
      &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    
      &lt;groupId&gt;com.mycompany&lt;/groupId&gt;
      &lt;artifactId&gt;appmaker&lt;/artifactId&gt;
      &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
      &lt;description&gt;Spring Boot Stock App&lt;/description&gt;
      &lt;name&gt;app&lt;/name&gt;
  
      &lt;properties&gt;
          &lt;java.version&gt;1.8&lt;/java.version&gt;
      &lt;/properties&gt;
  
      &lt;parent&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
          &lt;version&gt;2.1.4.RELEASE&lt;/version&gt;
      &lt;/parent&gt;
  
      &lt;dependencies&gt;
         &lt;dependency&gt;
              &lt;groupId&gt;javax.transaction&lt;/groupId&gt;
              &lt;artifactId&gt;jta&lt;/artifactId&gt;
              &lt;version&gt;1.1&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.apache.tomcat&lt;/groupId&gt;
              &lt;artifactId&gt;tomcat-dbcp&lt;/artifactId&gt;
              &lt;version&gt;8.0.5&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.thymeleaf&lt;/groupId&gt;
              &lt;artifactId&gt;thymeleaf-spring4&lt;/artifactId&gt;
              &lt;version&gt;3.0.9.RELEASE&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
              &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;com.h2database&lt;/groupId&gt;
              &lt;artifactId&gt;h2&lt;/artifactId&gt;
              &lt;scope&gt;runtime&lt;/scope&gt;
          &lt;/dependency&gt;        
          &lt;dependency&gt;
              &lt;groupId&gt;com.googlecode.json-simple&lt;/groupId&gt;
              &lt;artifactId&gt;json-simple&lt;/artifactId&gt;
              &lt;version&gt;1.1.1&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
              &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
              &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
              &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
              &lt;scope&gt;test&lt;/scope&gt;
          &lt;/dependency&gt;
      &lt;/dependencies&gt;
  
      &lt;build&gt;
          &lt;plugins&gt;
              &lt;plugin&gt;
                  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                  &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
              &lt;/plugin&gt;
          &lt;/plugins&gt;
      &lt;/build&gt;
  &lt;/project&gt;
  </code></pre>
  <p>Updated with code that calls AEarningsService:</p>
  <pre><code>package com.example.mycompany.beans.helpers;
  
  import com.example.mycompany.beans.AnnualEarnings;
  import com.example.mycompany.beans.Token;
  import com.example.mycompany.beans.Url;
  import com.example.mycompany.service.AnnualEarningsService;
  import java.io.IOException;
  import java.math.BigDecimal;
  import java.math.RoundingMode;
  import java.time.LocalDate;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import org.json.simple.JSONArray;
  import org.json.simple.JSONObject;
  import org.json.simple.parser.JSONParser;
  import org.json.simple.parser.ParseException;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.stereotype.Controller;
  
  @Controller
  public class AnnualHelper {
  
      private static final BigDecimal HUNDRED = new BigDecimal(100);
      private static final String EARNINGS_4_TAG = &quot;/earnings/4?period=annual&amp;token=&quot;;
      private static final String EARNINGS_1_TAG = &quot;/earnings/1?period=annual&amp;token=&quot;;
      private Url url;
  
      @Autowired
      private AnnualEarningsService annualEarningsService;
      
      // Call class methods, filter for earnings of size &gt; 0 for initial run
      public List&lt;AnnualEarnings&gt; run(String domain, String token, BigDecimal percent) {
  
          // Holds results
          List&lt;AnnualEarnings&gt; results = new ArrayList&lt;&gt;();
  
          // Grab symbols
          ...
  
          // Loop through incoming symbols
          symbols.forEach((symbol) -&gt; {
  
              // Grab most recent fiscalPeriod for symbol
              List&lt;AnnualEarnings&gt; exists = annualEarningsService.findBySymbol(symbol);
  
              // Check if symbol is in db
              // If exists, log
              if (!exists.isEmpty()) {
                  log(...);
  
              // Else, process new symbol
              } else {
              
                  try {
                      List&lt;AnnualEarnings&gt; earnings = processEarnings(symbol, percent);
  
                      // Filter for List&lt;AnnualEarnings&gt; greater than zero, add to results
                      if (!earnings.isEmpty()) {
  
                          // Add to results
                          earnings.forEach(e -&gt; results.add(e));
                      }
                  } catch (Exception ex) {
                      logInfo(&quot;runAEInitial() - Exception: &quot;, ex);
                  }
              }
          });
  
          return results;
      }
  
      // For one symbol, parse, save, update initial earnings
      private List&lt;AnnualEarnings&gt; processEarnings(String symbol, BigDecimal percent) {
  
          // Create Url
          ...
  
          // Grab api response
          String response = &quot;&quot;;
          try {
              response = CommonUtils.callApi(url);
          } catch (IOException ex) {
              log(...);
          }
  
          // Catch empty json object returned from API
          if (response.length() == 2) {
              return Collections.emptyList();
          }
  
          // Parse json from api, save to db
          parseAndSaveEarnings(response);
  
          // Update AnnualEarnings Objects
          updateEarnings(symbol);
  
          // From db, grab symbol's eearnings for last 4 quarters
          List&lt;AnnualEarnings&gt; updatedEarnings = annualEarningsService.findBySymbol(symbol);
  
          return filterByDiffPercent(updatedEarnings, percent);
      }
      
      
      // Update previous, diff values of AnnualEarnings objects just created
      private void updateEarnings(String symbol) {
  
          // Grab all objects from db for symbol
          List&lt;AnnualEarnings&gt; inList = 
                  annualEarningsService.findBySymbol(symbol);
  
          // Update prev &amp; diff values of incoming AnnualEarnings object
          for (int i = 1; i &lt; inList.size(); i++) {
  
              // Existing object fiscalEndDate
              LocalDate fiscalEndDate = inList.get(i - 1).getFiscalEndDate();
  
              // Existing object fiscalPeriod
              String fiscalPeriod = inList.get(i - 1).getFiscalPeriod();
  
              // Get original
              BigDecimal current = inList.get(i - 1).getActualEPS();
  
              // Get previous
              BigDecimal prev = inList.get(i).getActualEPS();
  
              // Get diff 
              BigDecimal diff = current.subtract(prev);
  
              // Get diffPercent
              BigDecimal diffPercent = (diff.divide(prev, 4, RoundingMode.HALF_EVEN)).movePointRight(2);
              
              // Align num signs
              diffPercent = CommonUtils.alignSigns(diff, diffPercent);
  
              // Update
              annualEarningsService.update(symbol, fiscalEndDate, fiscalPeriod, prev, diff, diffPercent);
          }
      }
  
      // Parse json string returned from API, save to db
      private void parseAndSaveEarnings(String str) {
  
          // Create parser
          JSONParser parser = new JSONParser();
          JSONObject fullObj;
  
          try {
              // Full json object
              fullObj = (JSONObject) parser.parse(str);
  
              // First layer
              JSONArray earningsArray = (JSONArray) fullObj.get(&quot;earnings&quot;);
  
              // Symbol
              String jsonSymbol = fullObj.get(&quot;symbol&quot;).toString();
  
              // Loop through earnings for one symbol
              earningsArray.forEach((earningsJson) -&gt; {
  
                  logInfo(&quot;&quot;, &quot;&quot;);
  
                  // Create jsonobject
                  JSONObject jsonObject = (JSONObject) earningsJson;
  
                  // Grab values
                  String jsonFiscEndDateStr = jsonObject.get(&quot;fiscalEndDate&quot;).toString();
                  
                  LocalDate jsonFiscEndDate = CommonUtils.convertStringToLocalDateAnnualEarnings(jsonFiscEndDateStr);
                  
                  String jsonFiscPer = jsonObject.get(&quot;fiscalPeriod&quot;).toString();
  
                  // Create update object
                  AnnualEarnings e = new AnnualEarnings();
  
                  // Grab data, set properties
                  String actEps = jsonObject.get(&quot;actualEPS&quot;).toString();
  
                  e.setFiscalEndDate(jsonFiscEndDate);
                  e.setSymbol(jsonSymbol);
                  e.setActualEPS(new BigDecimal(actEps));
                  e.setFiscalPeriod(jsonFiscPer);
  
                  // Save 
                  annualEarningsService.save(e);
             });
          } catch (ParseException ex) {
              logSevere(&quot;parseAndSaveAEarningsInitial()&quot;, ex.getMessage());
          }
      }
      
      // Filter objects with earnings difference &gt; 25%
      private List&lt;AnnualEarnings&gt; filterByDiffPercent(List&lt;AnnualEarnings&gt; updatedEarnings, BigDecimal percent) {
  
          // Hold results
          List&lt;AnnualEarnings&gt; results = new ArrayList&lt;&gt;();
  
          
          // Loop through objects, filter out objects with diffPercent &gt; 25
          updatedEarnings.forEach((earn) -&gt; {
              
              // THIS IS WHERE I FIND THAT ENTITY OBJECTS RETURNED HAVE NOT BEEN
              //  UPDATED
              logInfo(&quot;filterByDiffPercent() - earn: &quot;, earn.toString());
  
              BigDecimal diffPercent = earn.getDiffPercent();
  
              if (diffPercent != null &amp;&amp; diffPercent.compareTo(percent) == 1) {
                  results.add(earn);
              }
          });
          return results;
      }
  }
  
  </code></pre>