Search code examples
javamethodsjava-7generic-programmingdatabase-deadlocks

Java 7 Pass Method as Paramater for generic SQL Deadlock check


In Java 7 is it possible pass the method as a paramater.

The method would be invoked and would encapsulated in a do while to check for sql deadlocks and retry.

I am trying to make a generic way of handling deadlocks when there is a lock on the table, as there is currently thousands of inserts and updates to replicate this across. At present it would only look for SQLException but this could be changed to getErrorCode of SQLException and target ER_LOCK_DEADLOCK.

The code below is just an example of what I am trying to achieve:

public void Object1Insert(Object1 object1){
    genericSQLRetry(insertObject1(object1));
}

public void Object2Insert(Object2 object2){
    genericSQLRetry(insertObject2(object2));
}

public void genericSQLRetry({method}){
    int retries = 5;
    boolean isException = false;
  do{
    try {
        isException = false;
        retries--;
        //Invoke method
        {method}
    }catch (SQLException e){
        isException = true;
    }

    if (isException & retries > 0){
        //getRandomSleep would be between 750-1250ms. This is to stop multiple retries at the same time.
        Thread.sleep(getRandomSleep());
    }
  }while (isException == true && retries > 0);
}

Solution

  • In Java 7 is it possible pass the method as a paramater.

    No. That's why Java 8 adds lambdas (including method references) and functional interfaces.

    But you can define the method in an interface, implement that interface in the calling class(es) (perhaps anonymous ones you instantiate locally), accept that interface in genericSQLRetry, and call the method on the instance you receive.

    In your case, you could re-use Runnable (which is what they did in Java 8 for lambdas with no parameters and no return value), and:

    public void Object1Insert(final Object1 object1) {
        genericSQLRetry(new Runnable {
            public void run() {
                insertObject1(object1);
            }
        });
    }
    
    public void Object2Insert(Object2 object2) {
        genericSQLRetry(new Runnable {
            public void run() {
                insertObject2(object2);
            }
        });
    }
    
    public void genericSQLRetry(Runnable retryable) {
        int retries = 5;
        boolean isException = false;
        do {
            try {
                isException = false;
                retries--;
                //Invoke method
                retryable.run();
            }
            catch (SQLException e) {
                isException = true;
            }
    
            if (isException & retries > 0) {
                //getRandomSleep would be between 750-1250ms. This is to stop multiple retries at the same time.
                Thread.sleep(getRandomSleep());
            }
        } while (isException == true && retries > 0);
    }
    

    Personally, though, I'd prefer my own interface that's more clear semantically:

    interface Retryable {
        void retry() {
        }
    }
    

    then use Retryable instead of Runnable.