001package com.avaje.ebean.config.dbplatform;
002
003import java.sql.Connection;
004import java.sql.PreparedStatement;
005import java.sql.ResultSet;
006import java.sql.SQLException;
007
008import javax.persistence.PersistenceException;
009import javax.sql.DataSource;
010
011import com.avaje.ebean.Transaction;
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015/**
016 * A very simple Database sequence based IdGenerator.
017 * <p>
018 * One which batch requests sequence Id's would be better for performance.
019 * </p>
020 */
021public class SimpleSequenceIdGenerator implements PlatformIdGenerator {
022
023  private static final Logger logger = LoggerFactory.getLogger(SimpleSequenceIdGenerator.class);
024
025  private final String sql;
026
027  private final DataSource dataSource;
028
029  private final String seqName;
030
031  /**
032   * Construct given a dataSource and sql to return the next sequence value.
033   */
034  public SimpleSequenceIdGenerator(DataSource dataSource, String sql, String seqName) {
035    this.dataSource = dataSource;
036    this.sql = sql;
037    this.seqName = seqName;
038  }
039
040  public String getName() {
041    return seqName;
042  }
043
044  public boolean isDbSequence() {
045    return true;
046  }
047
048  public void preAllocateIds(int batchSize) {
049    // just ignore this
050  }
051
052  public Object nextId(Transaction t) {
053
054    boolean useTxnConnection = t != null;
055
056    Connection c = null;
057    PreparedStatement pstmt = null;
058    ResultSet rset = null;
059    try {
060      c = useTxnConnection ? t.getConnection() : dataSource.getConnection();
061      pstmt = c.prepareStatement(sql);
062      rset = pstmt.executeQuery();
063      if (rset.next()) {
064        return rset.getInt(1);
065      } else {
066        String m = "Always expecting 1 row from " + sql;
067        throw new PersistenceException(m);
068      }
069    } catch (SQLException e) {
070      throw new PersistenceException("Error getting sequence nextval", e);
071
072    } finally {
073      if (useTxnConnection) {
074        closeResources(rset, pstmt, null);
075      } else {
076        closeResources(rset, pstmt, c);
077      }
078    }
079  }
080
081  private void closeResources(ResultSet rset, PreparedStatement pstmt, Connection c) {
082    try {
083      if (rset != null) {
084        rset.close();
085      }
086    } catch (SQLException e) {
087      logger.error("Error closing ResultSet", e);
088    }
089    try {
090      if (pstmt != null) {
091        pstmt.close();
092      }
093    } catch (SQLException e) {
094      logger.error("Error closing PreparedStatement", e);
095    }
096    try {
097      if (c != null) {
098        c.close();
099      }
100    } catch (SQLException e) {
101      logger.error("Error closing Connection", e);
102    }
103  }
104
105}