All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.avaje.ebean.config.dbplatform.SequenceIdGenerator Maven / Gradle / Ivy

There is a newer version: 9.5.1
Show newest version
package com.avaje.ebean.config.dbplatform;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import javax.persistence.PersistenceException;
import javax.sql.DataSource;

import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Database sequence based IdGenerator.
 */
public abstract class SequenceIdGenerator implements PlatformIdGenerator {

  private static final Logger logger = LoggerFactory.getLogger(SequenceIdGenerator.class);

  /**
   * Used to synchronise the idList access.
   */
  protected final Object monitor = new Object();

  /**
   * Used to synchronise background loading (loadBatchInBackground).
   */
  protected final Object backgroundLoadMonitor = new Object();

  /**
   * The actual sequence name.
   */
  protected final String seqName;

  protected final DataSource dataSource;

  protected final BackgroundExecutor backgroundExecutor;

  protected final ArrayList idList = new ArrayList(50);

  protected final int batchSize;

  protected int currentlyBackgroundLoading;

  /**
   * Construct given a dataSource and sql to return the next sequence value.
   */
  public SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
    this.backgroundExecutor = be;
    this.dataSource = ds;
    this.seqName = seqName;
    this.batchSize = batchSize;
  }

  public abstract String getSql(int batchSize);

  /**
   * Returns the sequence name.
   */
  public String getName() {
    return seqName;
  }

  /**
   * Returns true.
   */
  public boolean isDbSequence() {
    return true;
  }

  /**
   * If allocateSize is large load some sequences in a background thread.
   * 

* For example, when inserting a bean with a cascade on a OneToMany with many * beans Ebean can call this to ensure . *

*/ public void preAllocateIds(int allocateSize) { if (batchSize > 1 && allocateSize > batchSize) { // only bother if allocateSize is bigger than // the normal loading batchSize if (allocateSize > 100) { // max out at 100 for now allocateSize = 100; } loadLargeAllocation(allocateSize); } } /** * Called by preAllocateIds when we know that a large number of Id's is going * to be needed shortly. */ protected void loadLargeAllocation(final int allocateSize) { // preAllocateIds was called with a relatively large batchSize // so we will just go ahead and load those anyway in background backgroundExecutor.execute(new Runnable() { public void run() { loadMoreIds(allocateSize, null); } }); } /** * Return the next Id. *

* If a Transaction has been passed in use the Connection from it. *

*/ public Object nextId(Transaction t) { synchronized (monitor) { if (idList.isEmpty()) { loadMoreIds(batchSize, t); } Long nextId = idList.remove(0); if (batchSize > 1) { if (idList.size() <= batchSize / 2) { loadBatchInBackground(); } } return nextId; } } /** * Load another batch of Id's using a background thread. */ protected void loadBatchInBackground() { // single threaded processing... synchronized (backgroundLoadMonitor) { if (currentlyBackgroundLoading > 0) { // skip as already background loading logger.debug("... skip background sequence load (another load in progress)"); return; } currentlyBackgroundLoading = batchSize; backgroundExecutor.execute(new Runnable() { public void run() { loadMoreIds(batchSize, null); synchronized (backgroundLoadMonitor) { currentlyBackgroundLoading = 0; } } }); } } protected void loadMoreIds(final int numberToLoad, Transaction t) { ArrayList newIds = getMoreIds(numberToLoad, t); if (logger.isDebugEnabled()) { logger.debug("... seq:" + seqName + " loaded:" + numberToLoad + " ids:" + newIds); } synchronized (monitor) { for (int i = 0; i < newIds.size(); i++) { idList.add(newIds.get(i)); } } } /** * Get more Id's by executing a query and reading the Id's returned. */ protected ArrayList getMoreIds(int loadSize, Transaction t) { String sql = getSql(loadSize); ArrayList newIds = new ArrayList(loadSize); boolean useTxnConnection = t != null; Connection c = null; PreparedStatement pstmt = null; ResultSet rset = null; try { c = useTxnConnection ? t.getConnection() : dataSource.getConnection(); pstmt = c.prepareStatement(sql); rset = pstmt.executeQuery(); while (rset.next()) { newIds.add(rset.getLong(1)); } if (newIds.isEmpty()) { throw new PersistenceException("Always expecting more than 1 row from " + sql); } return newIds; } catch (SQLException e) { if (e.getMessage().contains("Database is already closed")) { String msg = "Error getting SEQ when DB shutting down " + e.getMessage(); logger.info(msg); System.out.println(msg); return newIds; } else { throw new PersistenceException("Error getting sequence nextval", e); } } finally { if (useTxnConnection) { closeResources(null, pstmt, rset); } else { closeResources(c, pstmt, rset); } } } /** * Close the JDBC resources. */ protected void closeResources(Connection c, PreparedStatement pstmt, ResultSet rset) { try { if (rset != null) { rset.close(); } } catch (SQLException e) { logger.error( "Error closing ResultSet", e); } try { if (pstmt != null) { pstmt.close(); } } catch (SQLException e) { logger.error("Error closing PreparedStatement", e); } try { if (c != null) { c.close(); } } catch (SQLException e) { logger.error("Error closing Connection", e); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy