Update Dinâmico JDBC

Boa Tarde,
Pessoal, alguém conhece o update dinâmico ?, estou querendo criar no meu DAO, um método que só atualize a coluna que eu enviar a informação, ex: quero atualizar apenas a coluna NOME e CPF, porém todas as colunas do banco são NOTNULL , estou com dificuldades de fazer a query, se alguém conhecer algum livro ou artigo agradeço muito vlw.

package br.com.ophos.dao.impl;
import br.com.ophos.dao.ClienteDAO;
import br.com.ophos.dao.DAOException;
import br.com.ophos.mobile.model.Cliente;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Victor Oliveira
 */
public class ClienteDAOImpl implements ClienteDAO {

    private Connection conn;

    private void open() throws ClassNotFoundException, SQLException {
        Class.forName("oracle.jdbc.OracleDriver");
        String dbURL = "jdbc:oracle:thin:@localhost:1521:XE";
        String username = "System";
        String password = "194!@#";
        conn = DriverManager.getConnection(dbURL, username, password);

    }

    private void close() {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException ex) {
                Logger.getLogger(ClienteDAOImpl.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    @Override
    public Collection<Cliente> list() throws DAOException {
        List<Cliente> lista = new ArrayList<>();
        try {
            open();
            try (PreparedStatement stmt = conn.prepareStatement("SELECT * FROM CLIENTE");
                    ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    // criando o objeto Contato
                    Cliente cliente = new Cliente();
                    cliente.setId(rs.getLong("ID"));
                    cliente.setNome(rs.getString("NOME"));
                    cliente.setCpf(rs.getLong("CPF"));
                    cliente.setProduto(rs.getString("PRODUTO"));
                    cliente.setLogradouro(rs.getString("LOGRADOURO"));
                    cliente.setN(rs.getString("N"));
                    cliente.setCidade(rs.getString("CIDADE"));
                    cliente.setPais(rs.getString("PAIS"));
                    cliente.setTelefone(rs.getLong("TELEFONE"));
                    cliente.setRazaosocial(rs.getString("RAZAOSOCIAL"));
                    cliente.setNomefantasia(rs.getString("NOMEFANTASIA"));
                    cliente.setUsername(rs.getString("USERNAME"));
                    cliente.setSenha(rs.getString("SENHA"));
                    lista.add(cliente);
                }
            }
            return lista;
        } catch (SQLException | ClassNotFoundException e) {
            throw new DAOException("Ocorreu um erro ao consultar os clientes. Erro: " + e.getMessage());
        } finally {
            close();
        }
    }

    @Override
    public Cliente find(Long id) throws DAOException {
        try {
            open();
            try (PreparedStatement stmt = conn.prepareStatement("SELECT * FROM CLIENTE WHERE ID = ?")) {
                stmt.setLong(1, id);
                ResultSet rs = stmt.executeQuery();

                while (rs.next()) {
                    // criando o objeto Contato
                    Cliente cliente = new Cliente();
                    cliente.setId(rs.getLong("ID"));
                    cliente.setNome(rs.getString("NOME"));
                    cliente.setCpf(rs.getLong("CPF"));
                    cliente.setProduto(rs.getString("PRODUTO"));
                    cliente.setLogradouro(rs.getString("LOGRADOURO"));
                    cliente.setN(rs.getString("N"));
                    cliente.setCidade(rs.getString("CIDADE"));
                    cliente.setPais(rs.getString("PAIS"));
                    cliente.setTelefone(rs.getLong("TELEFONE"));
                    cliente.setRazaosocial(rs.getString("RAZAOSOCIAL"));
                    cliente.setNomefantasia(rs.getString("NOMEFANTASIA"));
                    cliente.setUsername(rs.getString("USERNAME"));
                    cliente.setSenha(rs.getString("SENHA"));
                    return cliente;
                }
                return null;
            }
        } catch (ClassNotFoundException | SQLException ex) {
            throw new DAOException("Ocorreu um erro ao consultar o cliente. Erro: " + ex.getMessage());
        } finally {
            close();
        }
    }

    @Override
    public void create(Cliente obj) throws DAOException {
        try {
            // autoincremento
            // select max(id)+1 from cliente
            // sequence com trigger

            open();
            PreparedStatement stat;
            String SQLInsert = "INSERT INTO CLIENTE (ID,NOME,CPF,PRODUTO,LOGRADOURO,N,CIDADE,PAIS,TELEFONE,RAZAOSOCIAL,NOMEFANTASIA,USERNAME,SENHA) VALUES (idcliente.nextval,?,?,?,?,?,?,?,?,?,?,?,?)";
            stat = conn.prepareStatement(SQLInsert);
            //stat.setLong(1, obj.getId());
            stat.setString(1, obj.getNome());
            stat.setLong(2, obj.getCpf());
            stat.setString(3, obj.getProduto());
            stat.setString(4, obj.getLogradouro());
            stat.setString(5, obj.getN());
            stat.setString(6, obj.getCidade());
            stat.setString(7, obj.getPais());
            stat.setLong(8, obj.getTelefone());
            stat.setString(9, obj.getRazaosocial());
            stat.setString(10, obj.getNomefantasia());
            stat.setString(11, obj.getUsername());
            stat.setString(12, obj.getSenha());
            stat.executeUpdate();
        } catch (SQLException | ClassNotFoundException ex) {
            throw new DAOException("Ocorreu um erro ao criar o cliente. Erro: " + ex.getMessage());
        } finally {
            close();
        }
    }

    @Override
    public void update(Cliente obj) throws DAOException {
        try {
            open();
            PreparedStatement stat;
            String SQLUpdate = "UPDATE CLIENTE SET NOME = ?,SET CPF = ?,SET PRODUTO = ?,SET LOGRADOURO = ?,SET N = ?,SET CIDADE = ?,SET PAIS = ?,SET TELEFONE = ?,SET RAZAOSOCIAL = ?,SET NOMEFANTASIA = ?,SET USERNAME = ?, SET SENHA = ?, WHERE ID = ? ";
            stat = conn.prepareStatement(SQLUpdate);
            stat.setString(1, obj.getNome());
            stat.setLong(2, obj.getId());
            stat.setLong(3, obj.getCpf());
            stat.setString(4, obj.getProduto());
            stat.setString(5, obj.getLogradouro());
            stat.setString(6, obj.getN());
            stat.setString(7, obj.getCidade());
            stat.setString(8, obj.getPais());
            stat.setLong(9, obj.getTelefone());
            stat.setString(10, obj.getRazaosocial());
            stat.setString(11, obj.getNomefantasia());
            stat.setString(12, obj.getUsername());
            stat.setString(13, obj.getSenha());
            stat.execute();
            close();
        } catch (ClassNotFoundException | SQLException ex) {
            throw new DAOException("Ocorreu um erro ao atualizar o cliente. Erro: " + ex.getMessage());
        } finally {
            close();
        }
    }

    @Override
    public void delete(Long id) throws DAOException {
        try {
            open();
            PreparedStatement stat;
            String SQLDelete = "DELETE FROM CLIENTE WHERE ID = ?";
            stat = conn.prepareStatement(SQLDelete);
            stat.setLong(1, id);
            stat.execute();
        } catch (SQLException | ClassNotFoundException ex) {
            throw new DAOException("Ocorreu um erro ao apagar o cliente. Erro: " + ex.getMessage());
        } finally {
            close();
        }
    }
}

Basta criar um método específico pra atender essa funcionalidade, onde o SQL deve ficar assim:

stat = conn.prepareStatement("UPDATE CLIENTE SET NOME = ?, CPF = ? WHERE ID = ?");

Quer criar uma solução mágica “dinâmica” só complica, mistura responsabilidades. Na hora que o cliente pedir pra dar manutenção numa funcionalidade, essa mexida vai impactar outras funcionalidades.