Solve the problem of error SpringBatch / Cloud Task of SafeMode

Problem Description

Generally companies have DBA, DBA is likely to open the Safe mode, that is, it does not support the update operation is not indexed filter conditions.
The Spring Batch / Cloud Task JOB_SEQ have a table or tables TASK_SEQ, only one data can not complete the update operation.

Could not increment ID for BATCH_JOB_SEQ sequence table; nested exception is java.sql.SQLException:
You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column

solution

If the DBA database, then no matter how to change the database can not be solved. Unless a developer to provide their own self-built private library (not in the DBA Management).
A better solution is to replace the MySQLMaxValueIncrementer Spring JDBC

This class consists of DefaultDataFieldMaxValueIncrementerFactorycreation:

//DefaultDataFieldMaxValueIncrementerFactory.java
@Override
    public DataFieldMaxValueIncrementer getIncrementer(String incrementerType, String incrementerName) {
        DatabaseType databaseType = DatabaseType.valueOf(incrementerType.toUpperCase());

        if (databaseType == DB2 || databaseType == DB2AS400) {
            return new DB2SequenceMaxValueIncrementer(dataSource, incrementerName);
        }
        else if (databaseType == DB2ZOS) {
            return new DB2MainframeSequenceMaxValueIncrementer(dataSource, incrementerName);
        }
        else if (databaseType == DERBY) {
            return new DerbyMaxValueIncrementer(dataSource, incrementerName, incrementerColumnName);
        }
        else if (databaseType == HSQL) {
            return new HsqlMaxValueIncrementer(dataSource, incrementerName, incrementerColumnName);
        }
        else if (databaseType == H2) {
            return new H2SequenceMaxValueIncrementer(dataSource, incrementerName);
        }
        else if (databaseType == MYSQL) {
            MySQLMaxValueIncrementer mySQLMaxValueIncrementer = new MySQLMaxValueIncrementer(dataSource, incrementerName, incrementerColumnName);
            mySQLMaxValueIncrementer.setUseNewConnection(true);
            return mySQLMaxValueIncrementer;
        }
....
}

Then they would replace this DefaultDataFieldMaxValueIncrementerFactory, code is as follows:

package io.github.slankka.springbatch.safemode.patch;

import org.springframework.batch.item.database.support.DefaultDataFieldMaxValueIncrementerFactory;
import org.springframework.batch.support.DatabaseType;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;

import javax.sql.DataSource;

/**
 * project: springbatch safemode patch
 * <br/>To prevent error: <br/>
 * <code>
 * Could not increment ID for BATCH_JOB_SEQ sequence table; <br/>
 * nested exception is java.sql.SQLException: <br/>
 * You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column <br/>
 * </code>
 *
 * @author slankka on 2019/8/30.
 */
public class SafeModeMysqlIncreamentFactory extends DefaultDataFieldMaxValueIncrementerFactory {

    private DataSource dataSource;
    private String incrementerColumnName = "ID";

    public SafeModeMysqlIncreamentFactory(DataSource dataSource) {
        super(dataSource);
        this.dataSource = dataSource;
    }

    @Override
    public void setIncrementerColumnName(String incrementerColumnName) {
        super.setIncrementerColumnName(incrementerColumnName);
        this.incrementerColumnName = incrementerColumnName;
    }

    @Override
    public DataFieldMaxValueIncrementer getIncrementer(String incrementerType, String incrementerName) {
        DatabaseType databaseType = DatabaseType.valueOf(incrementerType.toUpperCase());
        if (databaseType == DatabaseType.MYSQL) {
            SafeModeMysqlMaxValueIncreamenter mySQLMaxValueIncrementer = new SafeModeMysqlMaxValueIncreamenter(dataSource, incrementerName, incrementerColumnName);
            mySQLMaxValueIncrementer.setUseNewConnection(true);
            return mySQLMaxValueIncrementer;
        }
        return super.getIncrementer(incrementerType, incrementerName);
    }
}

Here are a SafeModeMysqlMaxValueIncreamenter class, this class is the key to solve the problem:

MySQLMaxValueIncrementer direct copy of the code, modify stmt.executeUpdate SQL statements, tail added where columnName> 0.
This will fool the SafeMode check. In addition, if the field is not a primary key, he set as the primary key.

 @Override
    protected synchronized long getNextKey() throws DataAccessException {
        if (this.maxId == this.nextId) {
            /*
             * If useNewConnection is true, then we obtain a non-managed connection so our modifications
             * are handled in a separate transaction. If it is false, then we use the current transaction's
             * connection relying on the use of a non-transactional storage engine like MYISAM for the
             * incrementer table. We also use straight JDBC code because we need to make sure that the insert
             * and select are performed on the same connection (otherwise we can't be sure that last_insert_id()
             * returned the correct value).
             */
            Connection con = null;
            Statement stmt = null;
            boolean mustRestoreAutoCommit = false;
            try {
                if (this.useNewConnection) {
                    con = getDataSource().getConnection();
                    if (con.getAutoCommit()) {
                        mustRestoreAutoCommit = true;
                        con.setAutoCommit(false);
                    }
                } else {
                    con = DataSourceUtils.getConnection(getDataSource());
                }
                stmt = con.createStatement();
                if (!this.useNewConnection) {
                    DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
                }
                // Increment the sequence column...
                String columnName = getColumnName();
                try {
                    stmt.executeUpdate("update " + getIncrementerName() + " set " + columnName +
                            " = last_insert_id(" + columnName + " + " + getCacheSize() + ") where " +  columnName + " > 0");
                } catch (SQLException ex) {
                    throw new DataAccessResourceFailureException("Could not increment " + columnName + " for " +
                            getIncrementerName() + " sequence table", ex);
                }
                // Retrieve the new max of the sequence column...
                ResultSet rs = stmt.executeQuery(VALUE_SQL);
                try {
                    if (!rs.next()) {
                        throw new DataAccessResourceFailureException("last_insert_id() failed after executing an update");
                    }
                    this.maxId = rs.getLong(1);
                } finally {
                    JdbcUtils.closeResultSet(rs);
                }
                this.nextId = this.maxId - getCacheSize() + 1;
            } catch (SQLException ex) {
                throw new DataAccessResourceFailureException("Could not obtain last_insert_id()", ex);
            } finally {
                JdbcUtils.closeStatement(stmt);
                if (con != null) {
                    if (this.useNewConnection) {
                        try {
                            con.commit();
                            if (mustRestoreAutoCommit) {
                                con.setAutoCommit(true);
                            }
                        } catch (SQLException ignore) {
                            throw new DataAccessResourceFailureException(
                                    "Unable to commit new sequence value changes for " + getIncrementerName());
                        }
                        JdbcUtils.closeConnection(con);
                    } else {
                        DataSourceUtils.releaseConnection(con, getDataSource());
                    }
                }
            }
        } else {
            this.nextId++;
        }
        return this.nextId;
    }

Instructions

参见 slankka/spring-batch-safemode-patch

Guess you like

Origin www.cnblogs.com/slankka/p/11444304.html
Recommended