Spring JdbcTemplate在创建存储过程时抛出错误

时间:2015-11-09 08:09:20

标签: java mysql spring stored-procedures

我在为MariaDB创建SP时遇到了一个有趣的问题。 当我直接从HeidiSQL(MySql的db客户端应用程序)执行DDL以下时,我可以轻松创建SP。

DROP PROCEDURE IF EXISTS `sp_delete_merchant`;

CREATE PROCEDURE `sp_delete_merchant`(IN `mer_id` BIGINT)
BEGIN
  DECLARE stop BIT DEFAULT FALSE;
  DECLARE comm_id BIGINT;
  DECLARE commodities CURSOR FOR SELECT id FROM tbl_commodity WHERE merchant_id = mer_id;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET stop = TRUE;

  # DELETE RECORDS FROM tbl_package2commodity BASED ON COMMODITY ID
  OPEN commodities;
    read_loop: LOOP
      FETCH commodities INTO comm_id;
      DELETE FROM tbl_package2commodity WHERE commodity_id = comm_id;
      IF stop THEN
        LEAVE read_loop;
      END IF;
    END LOOP;
  CLOSE commodities;

  # DELETE RECORDS FROM OTHER TABLES BASED ON MERCHANT ID
  DELETE FROM tbl_commodity WHERE merchant_id = mer_id;
  DELETE FROM tbl_package WHERE merchant_id = mer_id;
  DELETE FROM tbl_contact_person WHERE merchant_id = mer_id;
  DELETE FROM tbl_sales_force WHERE merchant_id = mer_id;
  DELETE FROM tbl_merchant WHERE id = mer_id;
END;

但是当我尝试从Spring JdbcTemplate 执行上面的DDL时,如下代码:

public class DbStoreProceduresCreator implements InitializingBean {

    private static Logger log = LoggerFactory.getLogger(DbStoreProceduresCreator.class);

    @Value("classpath:com/litaal/commerce/config/setup_store_procedures.sql")
    private Resource createStoreProcedures;

    @Autowired
    private DataSource coreDS;

    @Override
    public void afterPropertiesSet() throws Exception {
        BufferedReader br = null;
        StringBuffer sql = new StringBuffer();
        try {
            InputStream is = createStoreProcedures.getInputStream();
            br = new BufferedReader(new InputStreamReader(is));
            String line;
            while ((line = br.readLine()) != null) {
                sql.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                br.close();
            }
        }
        if (sql.length() > 0) {
            try {
                JdbcTemplate tmp = new JdbcTemplate(coreDS);
                tmp.execute(sql.toString());
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
    }
}

它会抛出如下错误:

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1

我已经尝试使用谷歌搜索并改变我的脚本十次或更多次,但仍然无法找到我的DDL有什么问题。

我想从我的Java应用程序创建SP的原因是为了让我的应用程序尽可能便携,而无需对数据库执行任何操作。

3 个答案:

答案 0 :(得分:1)

事实证明,JdbcTemplate无法在选择/更新等过程中执行带有注释(#)(--)的SQL过程。JdbcTemplate执行此操作在语句的开头支持(--#)注释,以便:

-- --------------------------------------------
-- Summary Comments!
-- Note: This comes before a delimited statement (;). 
# These are okay too.
-- --------------------------------------------
SELECT * 
FROM User
-- This comment fails.
WHERE id > 0;

这是我如何分解程序(来自MySQL)。

<强>程序

USE `DbName`;
DROP procedure IF EXISTS `ProcName`;

DELIMITER $$
USE `DbName`$$
CREATE PROCEDURE `ProcName` ()
BEGIN
    # Here's a comment that would normally fail.
    SELECT 
        u.id        
    FROM
    User as u       
END$$

DELIMITER ;

<强>分析器

StringBuilder builder = new StringBuilder();
String[] lines = script.split("\n");

String delimiter = ";";

for (int i = 0; i < lines.length; i++) {
    String line = lines[i];

    if (line.contains("DELIMITER")) {
        String[] tokens = line.split(" ");
        delimiter = tokens[1];
        builder = new StringBuilder();
        continue;
    }

    //Comments cannot be executed properly by the JdbcTemplate, strip them out. 
    if (line.contains("#")) {               
        line = line.substring(0, line.indexOf('#'));
    }

    if (line.contains(delimiter)) {
        builder.append(line.replace(delimiter, ""));
        template.execute(builder.toString());
        builder = new StringBuilder();
    } else {
        builder.append(line);
    }
}

答案 1 :(得分:0)

也许您的代码存在一些问题,但其中一个问题是JdbcTemplate.execute(String sql)只执行一个语句

/**
 * Issue a single SQL execute, typically a DDL statement.
 * @param sql static SQL to execute
 * @throws DataAccessException if there is any problem
 */
void execute(String sql) throws DataAccessException;

因此,您必须将执行分为两次执行,一次针对DROP PROCEDURE IF EXISTS "sp_delete_merchant";,另一次针对其余的执行。

BTW:你应该看看org.springframework.jdbc.datasource.init.ScriptUtils

另一件让我感到担忧的事情是你的字符串引号。

答案 2 :(得分:0)

最后......我找到了答案。 问题在于 JdbcTemplate 如何执行SQL脚本。它逐行执行(由新行分隔)。所以我更改了部分代码以删除所有新行,如下面的

....

if (sql.length() > 0) {
    String query = sql.toString().replaceAll("\\n", " ");
    query = query.replaceAll("\\t", " ");
    log.info(query);
    try {
        JdbcTemplate tmp = new JdbcTemplate(dataSource);
        tmp.execute(query);
    } catch (Exception e) {
        log.error(e.getMessage());
    }
}
...

现在一切都像魅力一样。