Package siena.jdbc

Source Code of siena.jdbc.PostgresqlPersistenceManager

package siena.jdbc;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import siena.ClassInfo;
import siena.Generator;
import siena.Id;
import siena.QueryFilterSearch;
import siena.SienaException;
import siena.Util;
import siena.core.options.QueryOption;

public class PostgresqlPersistenceManager extends JdbcPersistenceManager {
  private static final String DB = "POSTGRES";
 
  public PostgresqlPersistenceManager() {
   
  }
 
  public PostgresqlPersistenceManager(ConnectionManager connectionManager, Class<?> listener) {
    super(connectionManager, listener);
  }
 
  @Override
    protected void setParameter(PreparedStatement ps, int index, Object value) throws SQLException {
        if (value != null && value instanceof Date) {
            Date date = (Date) value;
            ps.setTimestamp(index, new Timestamp(date.getTime()));
        } else {
            ps.setObject(index, value);
        }
    }

  @Override
  protected void insertWithAutoIncrementKey(JdbcClassInfo classInfo, Object obj) throws SQLException, IllegalAccessException {
    List<String> keyNames = new ArrayList<String>();
    for (Field field : classInfo.generatedKeys) {
      keyNames.add(field.getName());
    }

    ResultSet gk = null;
    PreparedStatement ps = null;
    try {
      ps = getConnection().prepareStatement(
          classInfo.insertSQL + " RETURNING " + Util.join(keyNames, ","));
      addParameters(obj, classInfo.insertFields, ps, 1);
      gk = ps.executeQuery();
      if (!gk.next())
        throw new SienaException("No such generated keys");

      int i = 1;
      for (Field field : classInfo.generatedKeys) {
        //field.setAccessible(true);
        Util.setFromObject(obj, field, gk.getObject(i));
        // field.set(obj, gk.getObject(i));
        i++;
      }
    } finally {
      JdbcDBUtils.closeResultSet(gk);
      JdbcDBUtils.closeStatementAndConnection(this, ps);
    }
  }
 
  /**
   * required to be overriden for Postgres
   *
   * @param classInfo
   * @param objMap
   * @throws SQLException
   * @throws IllegalAccessException
   */
  @Override
  protected int insertBatchWithAutoIncrementKey(JdbcClassInfo classInfo, Map<JdbcClassInfo, List<Object>> objMap) throws SQLException, IllegalAccessException {
    List<String> keyNames = new ArrayList<String>();
    for (Field field : classInfo.generatedKeys) {
      keyNames.add(field.getName());
    }
   
    // can't use batch in Postgres with generated keys... known bug
    // http://postgresql.1045698.n5.nabble.com/PreparedStatement-batch-statement-impossible-td3406927.html
    PreparedStatement ps = null;
    ResultSet gk = null;
    int res = 0;
    try {
      ps = getConnection().prepareStatement(
          classInfo.insertSQL + " RETURNING " + Util.join(keyNames, ","));
     
      for(Object obj: objMap.get(classInfo)){
        for (Field field : classInfo.keys) {
          Id id = field.getAnnotation(Id.class);
          if (id.value() == Generator.UUID) {
            field.set(obj, UUID.randomUUID().toString());
          }
        }
        // TODO: implement primary key generation: SEQUENCE
        addParameters(obj, classInfo.insertFields, ps, 1);
        gk = ps.executeQuery();
        if (!gk.next())
          throw new SienaException("No such generated keys");
 
        int i = 1;
        for (Field field : classInfo.generatedKeys) {
          //field.setAccessible(true);
          Util.setFromObject(obj, field, gk.getObject(i));
          // field.set(obj, gk.getObject(i));
          i++;
        }
       
        JdbcDBUtils.closeResultSet(gk);
        res++;
      }
     
    } finally {
      JdbcDBUtils.closeStatementAndConnection(this, ps);
    }
    // doesn't work with Postgres because it doesn't manage generated keys
    // int[] res = ps.executeBatch();
   
    return res;
  }
 
  @Override
  public <T> void appendSqlSearch(QueryFilterSearch qf, Class<?> clazz, JdbcClassInfo info, StringBuilder sql, List<Object> parameters) {
    List<String> cols = new ArrayList<String>();
    try {
      for (String field : qf.fields) {
        Field f = Util.getField(clazz, field);
       
        Class<?> cl = f.getType();
        // if a number or date, doesn't try to coalesce
        if(Number.class.isAssignableFrom(cl)
            ||
          Date.class.isAssignableFrom(cl)){
          String[] columns = ClassInfo.getColumnNames(f, info.tableName);
          for (String col : columns) {
            cols.add(col);
          }
        }
        // if is model, gets the key type and does the same as herebefore
        else if(ClassInfo.isModel(cl)) {
          ClassInfo ci = ClassInfo.getClassInfo(cl);
          if(ci.keys.size()==1){
            Field key = ci.keys.get(0);
            if(Number.class.isAssignableFrom(key.getType())
                ||
              Date.class.isAssignableFrom(key.getType())){
              cols.add(f.getName());
            }else {
              cols.add("coalesce("+f.getName()+", '')");
            }
          }
          else {
            for (Field key : ci.keys) {
              String[] columns = ClassInfo.getColumnNamesWithPrefix(key, f.getName()+"_");
              if(Number.class.isAssignableFrom(key.getType())
                  ||
                Date.class.isAssignableFrom(key.getType())){
                for (String col : columns) {
                  cols.add(col);
                }
              }else {
                for (String col : columns) {
                  cols.add("coalesce("+col+", '')");
                }
              }
            }
          }
         
        }
        else {
          String[] columns = ClassInfo.getColumnNames(f, info.tableName);
          for (String col : columns) {
            cols.add("coalesce("+col+", '')");
          }
        }
      }
      QueryOption opt = qf.option;
      if(opt != null){
        // only manages QueryOptionJdbcSearch
        if(QueryOptionPostgresqlSearch.class.isAssignableFrom(opt.getClass())){
          String lang = ((QueryOptionPostgresqlSearch)opt).language;
          if(lang != null && !"".equals(lang) ){
            sql.append("to_tsvector('"+lang+"', "+Util.join(cols, " || ' ' || ")+") @@ to_tsquery(?)");
          }
          else {
            sql.append("to_tsvector('english', "+Util.join(cols, " || ' ' || ")+") @@ to_tsquery(?)");
          }
        }else{
        }
      }else {
        sql.append("to_tsvector('english', "+Util.join(cols, " || ' ' || ")+") @@ to_tsquery(?)");
      }
      parameters.add(qf.match);
    }catch(Exception e){
      throw new SienaException(e);
    }
  }

  @Override
  public void save(Object obj) {   
    JdbcClassInfo classInfo = JdbcClassInfo.getClassInfo(obj.getClass());

    List<String> keyNames = new ArrayList<String>();
    for (Field field : classInfo.keys) {
      keyNames.add(field.getName());
    }
   
    PreparedStatement ps = null;
    try {
      Field idField = classInfo.info.getIdField();
      Object idVal = Util.readField(obj, idField);

      if (idVal == null) {
        insert(obj);
      } else {
        // !!! insert or update pour postgres : the less worst solution I found!!!!
        // INSERT INTO myTable (myKey) SELECT myKeyValue WHERE myKeyValue NOT IN (SELECT myKey FROM myTable);
        // UPDATE myTable SET myUpdateCol = myUpdateColValue WHERE myKey = myKeyValue;
        ps = getConnection().prepareStatement(
            "INSERT INTO "+ classInfo.tableName + " (" + Util.join(keyNames, ",") + ") "
            + "SELECT ? WHERE ? NOT IN (SELECT "+ Util.join(keyNames, ","
            + " FROM "+ classInfo.tableName + ");"
            + classInfo.updateSQL);
        int i = 1;
        i = addParameters(obj, classInfo.keys, ps, i);
        i = addParameters(obj, classInfo.keys, ps, i);
        i = addParameters(obj, classInfo.updateFields, ps, i);
        addParameters(obj, classInfo.keys, ps, i);
        ps.executeUpdate();       
      }
    } catch (SienaException e) {
      throw e;
    } catch (Exception e) {
      throw new SienaException(e);
    } finally {
      JdbcDBUtils.closeStatementAndConnection(this, ps);
    }
  }
 


  @Override
  public int save(Object... objects) {
    return save(Arrays.asList(objects));
  }

  @Override
  public int save(Iterable<?> objects) {
    Map<JdbcClassInfo, List<Object>> generatedObjMap = new HashMap<JdbcClassInfo, List<Object>>();
    Map<JdbcClassInfo, List<Object>> objMap = new HashMap<JdbcClassInfo, List<Object>>();
    PreparedStatement ps = null;
   
    for(Object obj:objects){
      JdbcClassInfo classInfo = JdbcClassInfo.getClassInfo(obj.getClass());
      Field idField = classInfo.info.getIdField();
      Object idVal = Util.readField(obj, idField);
     
      if(idVal == null && !classInfo.generatedKeys.isEmpty()){
        if(!generatedObjMap.containsKey(classInfo)){
          List<Object> l = new ArrayList<Object>();
          l.add(obj);
          generatedObjMap.put(classInfo, l);
        }else{
          generatedObjMap.get(classInfo).add(obj);
        }
      } else {
        if(!objMap.containsKey(classInfo)){
          List<Object> l = new ArrayList<Object>();
          l.add(obj);
          objMap.put(classInfo, l);
        }else{
          objMap.get(classInfo).add(obj);
        }
      }
    }
   
    int total = 0;
    try {
      // these are the insertion with generated keys
      for(JdbcClassInfo classInfo: generatedObjMap.keySet()){
        total += insert(generatedObjMap.get(classInfo));
      }
     
      // these are the insertion or update without generated keys
      // can't use batch in Postgres with generated keys... known bug
      // http://postgresql.1045698.n5.nabble.com/PreparedStatement-batch-statement-impossible-td3406927.html
      for(JdbcClassInfo classInfo: objMap.keySet()){
        List<String> keyNames = new ArrayList<String>();
        for (Field field : classInfo.keys) {
          keyNames.add(field.getName());
        }
       
        // !!! insert or update pour postgres : the less worst solution I found!!!!
        // INSERT INTO myTable (myKey) SELECT myKeyValue WHERE myKeyValue NOT IN (SELECT myKey FROM myTable);
        // UPDATE myTable SET myUpdateCol = myUpdateColValue WHERE myKey = myKeyValue;
        ps = getConnection().prepareStatement(
            "INSERT INTO "+ classInfo.tableName + " (" + Util.join(keyNames, ",") + ") "
            + "SELECT ? WHERE ? NOT IN (SELECT "+ Util.join(keyNames, ","
            + " FROM "+ classInfo.tableName + ");"
            + classInfo.updateSQL);
     
        for(Object obj: objMap.get(classInfo)){       
          int i = 1;
          i = addParameters(obj, classInfo.keys, ps, i);
          i = addParameters(obj, classInfo.keys, ps, i);
          i = addParameters(obj, classInfo.updateFields, ps, i);
          addParameters(obj, classInfo.keys, ps, i);
          ps.executeUpdate();
          total++;
        }
      }
     
      return total;     
    } catch (SienaException e) {
      throw e;
    } catch (Exception e) {
      throw new SienaException(e);
    } finally {
      JdbcDBUtils.closeStatementAndConnection(this, ps);
    }
  }
 
 
}
TOP

Related Classes of siena.jdbc.PostgresqlPersistenceManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.