Mysql 实现upsert已有更新操作

作者: 大猪大猪 | 来源:发表于2018-11-06 17:18 被阅读32次

    在看到了mongoTemplate的操作之后,觉得这种东西是很符合我们程序员世界的操作的,但是看到mysql的jdbc之后,瞬间一百万个小泥马从头飘过,所以就想自己实现一个mysql版本的upsert功能,有setincrease,decrease

    实现操作

    参考mongoTemplate,创建一个update.javaquery.java类,方便两款db之间转换

    import java.util.HashMap;
    import java.util.Map;
    
    public class Update {
    
        private Map<String, Object> sets = new HashMap<>();
        private Map<String, Object> incs = new HashMap<>();
    
        //省略get set操作
    }
    
    public class Query {
    
        private Map<String,Object> values = new HashMap<>();
    
        public Query equals(String name,Object value){
            values.put(name,value);
            return this;
        }
    
        //省略get set操作
    }
    

    只依赖一个包

    compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.39'
    

    MysqlClient 客户端工具
    代码非常精简,大家可以根据自己的喜好添加功能

    import java.sql.*;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.util.*;
    import java.util.stream.Collectors;
    
    public class MysqlClient {
    
        private Connection connection;
        private String jdbcUrl;
        private String driver = "com.mysql.jdbc.Driver";
    
        public MysqlClient(String jdbcUrl) {
            this.jdbcUrl = jdbcUrl;
            this.init();
        }
    
        public void init() {
            try {
                if (connection == null || connection.isClosed()) {
                    String[] split = jdbcUrl.split("\\|");
                    Class.forName(driver);
                    connection = DriverManager.getConnection(split[0], split[1], split[2]);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    
        public void upsert(String table, Query query, Update update) {
            List<String> names = new ArrayList<>();
            List<Object> params = new ArrayList<>();
            names.addAll(query.getValues().keySet().stream().map(item -> String.format("`%s`", item)).collect(Collectors.toList()));
            names.addAll(update.getSets().keySet().stream().map(item -> String.format("`%s`", item)).collect(Collectors.toList()));
            names.addAll(update.getIncs().keySet().stream().map(item -> String.format("`%s`", item)).collect(Collectors.toList()));
    
            List<String> values = new ArrayList<>();
            values.addAll(query.getValues().values().stream().map(item -> " ? ").collect(Collectors.toList()));
            params.addAll(query.getValues().values());
    
            values.addAll(update.getSets().values().stream().map(item -> " ? ").collect(Collectors.toList()));
            params.addAll(update.getSets().values());
    
            values.addAll(update.getIncs().values().stream().map(item -> " ? ").collect(Collectors.toList()));
            params.addAll(update.getIncs().values());
    
            List<String> updates = new ArrayList<>();
            update.getSets().forEach((key, value) -> {
                updates.add(String.format(" `%s` = ? ", key));
                params.add(value);
            });
            update.getIncs().forEach((key, value) -> {
                updates.add(String.format(" `%s` = `%s` + %s", key, key, value));
            });
    
            String sql = String.format("INSERT INTO `%s` (%s) VALUES(%s) ON DUPLICATE KEY UPDATE %s",
                    table,
                    String.join(",", names),
                    String.join(",", values),
                    String.join(",", updates)
            );
            this.execute(sql, params.toArray());
        }
    
        private void fillStatement(PreparedStatement statement, Object... params) throws SQLException {
            for (int i = 1, len = params.length; i <= len; i++) {
                Object value = params[i - 1];
                if (value instanceof String) {
                    statement.setString(i, value.toString());
                } else if (value instanceof Integer) {
                    statement.setInt(i, Integer.parseInt(value.toString()));
                } else if (value instanceof Boolean) {
                    statement.setBoolean(i, Boolean.parseBoolean(value.toString()));
                } else if (value instanceof LocalDate || value instanceof LocalDateTime) {
                    statement.setString(i, value.toString());
                } else if (value instanceof Long) {
                    statement.setLong(i, Long.parseLong(value.toString()));
                } else if (value instanceof Double) {
                    statement.setDouble(i, Double.parseDouble(value.toString()));
                } else if (value instanceof Float) {
                    statement.setDouble(i, Float.parseFloat(value.toString()));
                } else {
                    statement.setString(i, value.toString());
                }
            }
        }
    
        public void execute(String sql, Object... params) {
            try {
                PreparedStatement statement = connection.prepareStatement(sql);
                this.fillStatement(statement, params);
                statement.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    

    使用方法

    @Test
    public void testUpsert(){
            MysqlClient mysqlClient = new MysqlClient("jdbc:mysql://demo.com:3306/db?useUnicode=true&useSSL=false&autoReconnect=true|root|password");
            Update update = new Update();
            update.set("count", 0);
            update.inc("active", 1);
            Query query = new Query();
            query.equals("name", "abc");
    
            mysqlClient.upsert("test", query, update);
            //含义:对相同行的`name`='abc',对其字段`count`重置为0,`active`新增1
    }
    

    解析

    生成的sql语句会像这样子

    INSERT INTO `test` (`name`,`count`,`active`,`value`) VALUES( ? , ? , ? , ? ) ON DUPLICATE KEY UPDATE  `count` = ? , `active` = `active` + 1, `value` = `value` - 1
    

    并且使用了占位符号,增加特殊符号的解析容错能力

    相关文章

      网友评论

        本文标题:Mysql 实现upsert已有更新操作

        本文链接:https://www.haomeiwen.com/subject/suwuxqtx.html