From 9cf31d3bc492ccdc3fd489bdb727d23e5a7327de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9B=84=E5=B3=B0?= Date: Tue, 15 Aug 2017 17:56:20 +0800 Subject: [PATCH] add jigsaw-mysql-protobuf --- .../payment/mysql/JdbcProtobufTemplate.java | 416 ++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 jigsaw-mysql-protobuf/src/main/java/org/jigsaw/payment/mysql/JdbcProtobufTemplate.java diff --git a/jigsaw-mysql-protobuf/src/main/java/org/jigsaw/payment/mysql/JdbcProtobufTemplate.java b/jigsaw-mysql-protobuf/src/main/java/org/jigsaw/payment/mysql/JdbcProtobufTemplate.java new file mode 100644 index 0000000..45de833 --- /dev/null +++ b/jigsaw-mysql-protobuf/src/main/java/org/jigsaw/payment/mysql/JdbcProtobufTemplate.java @@ -0,0 +1,416 @@ +package org.jigsaw.payment.mysql; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.beanutils.ConvertUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.reflect.MethodUtils; +import org.jigsaw.payment.model.ColumnFieldOption; +import org.jigsaw.payment.model.ColumnType; +import org.jigsaw.payment.model.TableMessageOption; +import org.jigsaw.payment.model.Taglib; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.PreparedStatementCreator; +import org.springframework.jdbc.core.RowMapper; + +import com.google.protobuf.DescriptorProtos.FieldOptions; +import com.google.protobuf.Descriptors; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Message; + +/** + * 存取Protobuf message 数据对象到JDBC数据库中, 是对JdbcTemplate的一个封装。 + * + * @author shamphone@gmail.com + * @version 1.0.0 + * @date 2017年8月9日 + * @param + * ID的数据类型 + * @param + * message的数据类型 + */ +public class JdbcProtobufTemplate { + + private static Logger logger = LoggerFactory.getLogger(JdbcProtobufTemplate.class); + /** + * 数据库行映射到 protobuf message对象 + */ + public class ProtobufMessageRowMapper implements + RowMapper { + @SuppressWarnings("unchecked") + @Override + public N mapRow(ResultSet rs, int rowNum) throws SQLException { + Message.Builder builder = newBuilder(messageClass); + populate(rs, builder); + return (N) builder.build(); + } + } + + protected Class messageClass; + protected Descriptors.Descriptor descriptor; + protected JdbcTemplate jdbcTemplate; + protected String tableName; + + public JdbcProtobufTemplate(JdbcTemplate jdbcTemplate) { + this(jdbcTemplate, null); + } + + public JdbcProtobufTemplate(JdbcTemplate jdbcTemplate, Class messageClass) { + this(jdbcTemplate, messageClass, null); + } + + public JdbcProtobufTemplate(JdbcTemplate jdbcTemplate, + Class messageClass, String tableName) { + this.jdbcTemplate = jdbcTemplate; + if (messageClass == null) { + this.messageClass = this.parseMessageClass(); + } else { + this.messageClass = messageClass; + } + this.descriptor = this.getDescriptor(messageClass); + if (tableName == null) { + this.tableName = this.parseTableName(); + } else { + this.tableName = tableName; + } + + } + + private Class parseMessageClass() { + Type genType = getClass().getGenericSuperclass(); + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + for (Type type : params) {// 查找 message类 + if (type instanceof Class + && Message.class.isAssignableFrom((Class) type)) { + return (Class) type; + } + } + return null; + } + + private String parseTableName() { + TableMessageOption tableMessageOption = descriptor.getOptions() + .getExtension(Taglib.tableOption); + // 默认从protobuf配置中查询表名 + if (tableMessageOption != null + && StringUtils.isNotBlank(tableMessageOption.getTableName())) { + return tableMessageOption.getTableName(); + } + return null; + } + + private Descriptors.Descriptor getDescriptor(Class messageClass) { + try { + return (Descriptors.Descriptor) MethodUtils.invokeStaticMethod( + messageClass, "getDescriptor"); + } catch (NoSuchMethodException | IllegalAccessException + | InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + + /** + * 查询单个记录 + * + * @param sql + * @param args + * @return + */ + public M get(String sql, Object... args) { + logger.debug(sql); + try { + return jdbcTemplate.queryForObject(sql, + new ProtobufMessageRowMapper(), args); + } catch (EmptyResultDataAccessException ex) { + return null; + } + } + + /** + * 查询多个记录 + * + * @param sql + * @param args + * @return + */ + public List query(String sql, Object... args) { + logger.debug(sql); + return jdbcTemplate.query(sql, new ProtobufMessageRowMapper(), args); + } + + /** + * update method + */ + public int update(String sql, Object... args) { + logger.debug(sql); + return jdbcTemplate.update(sql, args); + } + + /** + * + * @param sql + * @param args + * @return + */ + public int update(final String sql, final List args) { + logger.debug(sql); + return jdbcTemplate.update(new PreparedStatementCreator() { + + @Override + public PreparedStatement createPreparedStatement(Connection con) + throws SQLException { + PreparedStatement ps = con.prepareStatement(sql); + populate(ps, args); + return ps; + } + + }); + } + + /** + * set preparedstatement params + * + * @param ps + * @param args + * @return + * @throws SQLException + */ + private void populate(PreparedStatement ps, List args) + throws SQLException { + for (int i = 0; i < args.size(); i++) { + Object o = args.get(i); + if (o instanceof Integer) { + ps.setInt(i + 1, (int) o); + } else if (o instanceof Long) { + ps.setLong(i + 1, (long) o); + } else if (o instanceof String) { + ps.setString(i + 1, (String) o); + } else if (o instanceof Date) { + ps.setDate(i + 1, (Date) o); + } else if (o instanceof Float) { + ps.setFloat(i + 1, (Float) o); + } else if (o instanceof Double) { + ps.setDouble(i + 1, (Double) o); + } else if (o instanceof Date) { + ps.setDate(i + 1, (Date) o); + } else if (o instanceof Timestamp) { + ps.setTimestamp(i + 1, (Timestamp) o); + } else if (o instanceof Descriptors.EnumValueDescriptor) { + ps.setInt(i + 1, + ((Descriptors.EnumValueDescriptor) o).getNumber()); + } + } + } + + /** + * + * @param rs + * @param builder + * @throws SQLException + */ + private void populate(ResultSet rs, Message.Builder builder) + throws SQLException { + ResultSetMetaData metaData = rs.getMetaData(); + int columnCount = metaData.getColumnCount();// 列个数 + String columnLabel = null;// 列名 + Object columnValue = null;// 列值 + Descriptors.FieldDescriptor fieldDescriptor = null; + for (int i = 1; i <= columnCount; i++) { + columnLabel = metaData.getColumnLabel(i); + columnValue = rs.getObject(i); + if (columnValue == null) + continue;// 如果为空,继续下一个 + fieldDescriptor = descriptor.findFieldByName(columnLabel); + if (fieldDescriptor == null) + continue;// 如果为空,继续下一个 + // 转换为相应的类型 ,会自动将 date 类型转换为long + if (fieldDescriptor.getType().equals(FieldDescriptor.Type.ENUM)) { + columnValue = fieldDescriptor.getEnumType().findValueByNumber( + (int) columnValue); + } else { + columnValue = ConvertUtils.convert(columnValue, fieldDescriptor + .getDefaultValue().getClass()); + } + builder.setField(fieldDescriptor, columnValue); + } + } + + /** + * 插入对象到默认的表中 + * + * @param message + * @return + */ + public long insert(M message) { + return insert(message, this.tableName); + } + + /** + * 插入对象到给定的表中。注意,这里并不自动生成ID。 + * + * @param message + * @param tableName + * @return + */ + public long insert(M message, String tableName) { + StringBuilder insertSql = new StringBuilder("insert ignore into "); + insertSql.append(tableName).append("("); + StringBuilder fields = new StringBuilder(""); + StringBuilder values = new StringBuilder(""); + List args = new ArrayList(); + Map fieldMap = message.getAllFields(); + + for (Entry entry : fieldMap.entrySet()) { + FieldDescriptor fieldDescriptor = entry.getKey(); + FieldOptions fieldOptions = fieldDescriptor.getOptions(); + ColumnFieldOption columnFieldOption = fieldOptions + .getExtension(Taglib.columnOption); + String fieldName = fieldDescriptor.getName(); + Object value = entry.getValue(); + + if (columnFieldOption.getColumnType() == ColumnType.DATETIME + || columnFieldOption.getColumnType() == ColumnType.TIMESTAMP) {// datetime类型 + if (value != null && (long) value > 0) { + fields.append(fieldName).append(","); + values.append("?, "); + args.add(new Timestamp((long) value)); + } + } else { + fields.append(fieldName).append(","); + values.append("?, "); + args.add(value); + } + } + int tmpIndex = fields.lastIndexOf(","); + insertSql.append(fields.substring(0, tmpIndex)).append(") values("); + tmpIndex = values.lastIndexOf(","); + insertSql.append(values.substring(0, tmpIndex)).append(")"); + String sql = insertSql.toString(); + logger.debug(sql); + return update(sql, args); + } + + /** + * + * @param message + * @param conditionFields + * @param conditionParams + * @return + */ + protected int updateMessageByCondition(M message, String[] conditionFields, + Object[] conditionParams) { + return updateMessageByCondition(message, conditionFields, + conditionParams, this.tableName); + } + + /** + * + * @param message + * @param conditionFields + * @param conditionParams + * @param tableName + * @return + */ + protected int updateMessageByCondition(M message, String[] conditionFields, + Object[] conditionParams, String tableName) { + StringBuilder updateSql = new StringBuilder("update "); + updateSql.append(tableName).append(" set "); + StringBuilder options = new StringBuilder(""); + List args = new ArrayList(); + Map fieldMap = message.getAllFields(); + + for (Entry entry : fieldMap.entrySet()) { + FieldDescriptor fieldDescriptor = entry.getKey(); + if (!Arrays.asList(conditionFields).contains( + fieldDescriptor.getName())) { + FieldOptions fieldOptions = fieldDescriptor.getOptions(); + ColumnFieldOption columnFieldOption = fieldOptions + .getExtension(Taglib.columnOption); + String fieldName = fieldDescriptor.getName(); + Object value = entry.getValue(); + if (columnFieldOption.getColumnType() == ColumnType.DATETIME + || columnFieldOption.getColumnType() == ColumnType.TIMESTAMP) {// datetime类型 + if (value != null && (long) value > 0) { + options.append(fieldName).append("=?, "); + args.add(new Timestamp((long) value)); + } + } else { + options.append(fieldName).append("=?, "); + args.add(value); + } + } + } + int tmpIndex = options.lastIndexOf(","); + updateSql.append(options.substring(0, tmpIndex)).append(" where 1=1 "); + StringBuilder condition = new StringBuilder(); + if (conditionFields.length != conditionParams.length) { + throw new IllegalArgumentException("condition error"); + } else { + for (int i = 0; i < conditionFields.length; i++) { + condition.append("AND ").append(conditionFields[i]) + .append("=? "); + args.add(conditionParams[i]); + } + updateSql.append(condition); + String sql = updateSql.toString(); + logger.debug(sql); + return update(sql, args); + } + } + + /** + * 获取查询的属性 + * + * @return + */ + protected String buildSelectStatement() { + StringBuilder statement = new StringBuilder(); + List fields = descriptor.getFields(); + for (int i = 0; i < fields.size(); i++) { + if (i == fields.size() - 1) { + statement.append(fields.get(i).getName()); + } else { + statement.append(fields.get(i).getName()).append(","); + } + } + return statement.toString(); + } + + /** + * get Builder from messageClass + * + * @param messageClass + * @return + * @throws InvocationTargetException + * @throws IllegalAccessException + * @throws NoSuchMethodException + */ + public static T.Builder newBuilder(Class messageClass) { + T.Builder builder = null; + try { + builder = (T.Builder) MethodUtils.invokeStaticMethod(messageClass, + "newBuilder"); + } catch (NoSuchMethodException | IllegalAccessException + | InvocationTargetException e) { + throw new RuntimeException(e); + } + return builder; + } +}