处理通用preparedStatement中的日期

时间:2017-12-28 10:52:35

标签: java date jdbc prepared-statement

构建泛型prepareStatement时遇到麻烦:我有8个SQL表,它们都以相同的方式操作,因此我想构建一个唯一的管理器,它可以插入/选择8个表中的任何一个。

为此,每个表都有一个描述符,可以在插入时提供表的字段,名称和值数组。

在管理器中,要插入的预准备语句具有以下形式:

"INSERT INTO " + table_name + " VALUES (?)"

然后,我用

之类的东西填补空白
myPreparedStatement.setString(1, values.getAllValues());

getAllValues()方法必须返回一个包含每个字段的字符串,例如“'This','Is',3,'example'”。 我对字符串和数字没有问题,但我无法在这些值中添加任何日期......

以2008年9月3日为例,我使用了以下格式: 2008-09-03, 03年8月9日, 080903, 03092018,但都失败了。 “yyMMdd”格式似乎是我在这里和那里看到的最佳选择,但我有错误:

"java.sql.SQLDataException: ORA-01843: not a valid month"

我不知道为什么......之前有人遇到过这个问题吗?

我知道这里有很多帖子讨论在数据库中插入日期,但它们都使用

preparedStatement.setDate(pos, Date);

声明,我不能这样做,因为日期在我的所有表格中都不是相同的位置。

编辑:

正如评论中所述,这是一个简单的样本,可以重现我正在尝试做的事情。如果你想再现,我让你处理连接和数据库设置:

public class Sample {

public void saveAll() throws ServiceException {

    Connection c = null;
    PreparedStatement ps = null;
    String sql = "INSERT INTO " + getTableName() +" VALUES (?)";

    try {
        c = getConnection();
        c.setAutoCommit(false);

        batch = c.prepareStatement(sql);
        batch.setString(getAllFieldValues());

        int res = batch.executeUpdate();
        c.commit();

    } catch (BatchUpdateException b) {
        throw new ServiceException("Erreur lors de l'exécution du batch", b);
    } catch (SQLException s) {
        throw new ServiceException("Impossible de sauvegarder les beans en base.", s);
    } finally {
        getManager().close(batch);
        freeConnection(c);
    }
}

public String getAllFieldValues() {
        return "'Hello', 'World', 42, '171228'"; 
}

public String getTableName() {
    return "myTableName";
}

}

3 个答案:

答案 0 :(得分:1)

JDBC中没有generic preparedStatement这样的东西。要在表T中插入四列,必须使用

 INSERT into T (col1,col2,col3,col4) values (?,?,?,?)

可能省略第一个包含列名的列表,但这是一个不良做法,因为您信任可能会更改的表的实际列。

仅使用

 INSERT into T  values (?,?,?,?)

正常工作,直到有人通过添加或删除列来修改表,然后才会失败。

所有绑定变量必须使用setXXX方法设置为额外的,并且列的相应类型和索引以1开头。

stmt.setInt(1,100)
stmt.setString(2,'xxx') 

答案 1 :(得分:0)

如果我能正确理解你的问题。为了在预准备语句中动态放置日期值,您可以覆盖setString()方法以使您的自定义代码检查日期值,否则。

或者更确切地说,如果您还可以使用本地方法来检查来的字符串是否为格式化日期。

为此您可以简单地传递Date String并附加一些前缀,以便您可以在自定义setString()方法中进行检查。

setString(String string, int position){
if(string.contains("SPECIFIC_PREFIX_CONSTANT")){
//batch.setDate(position, string.substring("REMOVE PREFIX AND ATTACH"));
}else{
//batch.setString(position, string);
}
}

答案 2 :(得分:0)

好的,我设法让我的东西工作,非常感谢你们所有人! 如果有人在我的问题上结束,我会回顾我现在的代码,这有效:)

因此,如前所述,我们有一个管理器与数据库交互,并且不知道他与之交互的表格。

以下是此经理的保存方法的代码:

public void saveAll(AbstractBeanClass[] values, String refSelected) {
    // connexion setup
    Connection c = null;
    PreparedStatement batch = null;
    // fetch table's fields, to prepare the placeholders
    String fields = values[0].getAllFields();
    String sql = "INSERT INTO " + values[0].getTableName() + " (" + fields + ") VALUES (";
    StringBuffer places = new StringBuffer();
    int [] res = null;
    // Start at 1 to have one field left, to end the parenthesis
    for(int i = 1; i < values[0].getNumberOfFields(); i++) {
        places.append("?, ");
    }
    // last field
    places.append("?)");

    sql = sql.concat(places.toString());  // We now have a full (?, ..., ?) 

    try {
        c = getConnection();
        c.setAutoCommit(false);
        batch = c.prepareStatement(sql);

        // Filling the batch
        int j = 1;
        for(AbstractBeanClass bean : values) {
            int i = 1;
            for(String type : bean.getAllTypes()) {
                switch(type) {
                    case "int" : {
                        batch.setInt(i, (int) bean.getOrderedValue(i)); 
                    }
                    break;
                    case "String" : {
                        batch.setString(i, (String)bean.getOrderedValue(i));
                    }
                    break;
                    case "Date" : {
                        batch.setDate(i, (java.sql.Date) bean.getOrderedValue(i));
                    }
                    break;
                }
                i++;
            }
            batch.addBatch();
            // In case of numerous insertions, some Databases don't allow more than 1000 inserts at a time
            if(j%1000 == 0) {
                res = batch.executeBatch();
                for(int k : res) {
                    if(k == Statement.EXECUTE_FAILED) {
                        getManager().close(batch);
                        freeConnection(c);
                        throw new RuntimeException("Error while inserting values.");
                    }
                }
            }
            j++;
        }
        // last execution
        res = batch.executeBatch();
        for(int i : res) {
            if(i == Statement.EXECUTE_FAILED) {
                getManager().close(batch);
                freeConnection(c);
                throw new RuntimeException("Error while inserting values in database.");
            }
        }

        c.commit();
        logger.debug("Insertion succeeded, we inserted " + j + " lines.");

    } catch (BatchUpdateException b) {
        throw new RuntimeException("Error in batch : ", b);
    } catch (SQLException s) {
        throw new RuntimeException("Error : we couldn't save the values : ", s);
    } finally {
        getManager().close(batch);
        freeConnection(c);
    }
}

所以这是程序的主要部分,但它需要表描述符。为了简单起见,我创建了一个抽象类来声明我需要的方法,并且所有表描述符都扩展了这个类,这里​​是声明:

package com.fr.sncf.fret.boctarification.domaine.referentiel;

import java.io.Serializable;
import java.text.SimpleDateFormat;

public abstract class DaoGenericReferentielBean implements Serializable {

private static final long serialVersionUID = 1L;
protected String allFields;
// the date Format used to insert the dates in base
protected final SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd");

public DaoGenericReferentielBean() {
    // empty constructor
}

/**
 * Return all columns' names, ordered according to database's order
 * @return
 */
public String getAllFields() {
    return this.allFields;
}

/**
 * Returns all values ordered by columns' order
 * @return String
 */
public abstract String getAllFieldsValues();

/**
 * @return the table name
 */
public abstract String getTableName();

/**
 * @return the number of field in this table
 */
public abstract int getNumberOfFields();

/**
 * Returns the ordered list of column's type
 */
public abstract String[] getAllTypes();

/**
 * Return the value corresponding to the given index
 * Values are treated here according to the database's columns order
 * @param index the column's number
 * @return an Object, either an int, or a String, or a Date
 */
public abstract Object getOrderedValue(int index);

}

现在你只需要根据这个模型描述你的桌子,希望它有所帮助!