001package io.ebean.config.dbplatform;
002
003import io.ebean.AcquireLockException;
004import io.ebean.DataIntegrityException;
005import io.ebean.DuplicateKeyException;
006import io.ebean.SerializableConflictException;
007
008import javax.persistence.PersistenceException;
009import java.sql.SQLException;
010import java.util.Collections;
011import java.util.Map;
012
013/**
014 * Translate SQLException based on SQLState codes.
015 */
016public class SqlCodeTranslator implements SqlExceptionTranslator {
017
018  private final Map<String, DataErrorType> map;
019
020  /**
021   * Create given the map of SQLState codes to error types.
022   */
023  public SqlCodeTranslator(Map<String, DataErrorType> map) {
024    this.map = map;
025  }
026
027  /**
028   * Create "No-op" implementation.
029   */
030  public SqlCodeTranslator() {
031    this.map = Collections.emptyMap();
032  }
033
034  private DataErrorType getErrorType(SQLException e) {
035    DataErrorType errorType = map.get(e.getSQLState());
036    if (errorType == null) {
037      // fall back to error code
038      errorType = map.get(String.valueOf(e.getErrorCode()));
039    }
040    return errorType;
041  }
042
043  @Override
044  public PersistenceException translate(String message, SQLException e) {
045
046    DataErrorType errorType = getErrorType(e);
047    // for DB2 we must inspect the sql exception chain to determine which
048    // persistence error occurred in a batch execution. We also concatenate
049    // the error messages to improve error analysis.
050    SQLException chain = e.getNextException();
051    if (chain != null) {
052      StringBuilder sb = new StringBuilder(message);
053      int i = 1;
054      while (chain != null && i < 100000) { // prevents from endless loop
055        sb.append("\n\t#").append(i++).append(": ").append(chain.getMessage());
056        if (errorType == null) {
057          errorType = getErrorType(chain);
058          if (errorType != null) {
059            sb.append(" (causing error)"); // mark the line, where we found a matching error code
060          }
061        }
062        chain = chain.getNextException();
063      }
064      message = sb.toString();
065    }
066
067    if (errorType != null) {
068      switch (errorType) {
069        case AcquireLock:
070          return new AcquireLockException(message, e);
071        case DuplicateKey:
072          return new DuplicateKeyException(message, e);
073        case DataIntegrity:
074          return new DataIntegrityException(message, e);
075        case SerializableConflict:
076          return new SerializableConflictException(message, e);
077      }
078    }
079    // return a generic exception
080    return new PersistenceException(message, e);
081  }
082}