package org.rapidoid.jdbc; import org.rapidoid.RapidoidThing; import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Since; import org.rapidoid.beany.Beany; import org.rapidoid.beany.Prop; import org.rapidoid.commons.StringRewriter; import org.rapidoid.lambda.Mapper; import org.rapidoid.u.U; import org.rapidoid.util.Msc; import org.rapidoid.util.TUUID; import org.rapidoid.util.WebData; import java.sql.*; import java.util.List; import java.util.Map; import java.util.UUID; /* * #%L * rapidoid-sql * %% * Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ @Authors("Nikolche Mihajlovski") @Since("5.3.0") public class JdbcUtil extends RapidoidThing { private static final StringRewriter NAMED_PARAMS_REWRITER = new StringRewriter(StringRewriter.ALL_QUOTES, "\\$(\\w+)\\b"); public static PreparedStatement prepare(Connection conn, String sql, final Map<String, ?> namedArgs, Object[] args) { try { if (namedArgs != null) { U.must(args == null); List<Object> arguments = U.list(); sql = substituteNamedParams(sql, namedArgs, arguments); args = arguments.toArray(); } PreparedStatement stmt = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); bind(stmt, args); return stmt; } catch (SQLException e) { throw new RuntimeException("Cannot create prepared statement!", e); } } private static String substituteNamedParams(String sql, final Map<String, ?> namedArgs, final List<Object> arguments) { return NAMED_PARAMS_REWRITER.rewrite(sql, new Mapper<String[], String>() { @Override public String map(String[] groups) throws Exception { String name = groups[1]; if (namedArgs.containsKey(name)) { Object value = namedArgs.get(name); arguments.add(value); return "?"; } else { return groups[0]; // not in the args -> leave it untouched } } }); } public static void bind(PreparedStatement stmt, Object[] args) throws SQLException { for (int i = 0; i < args.length; i++) { Object arg = args[i]; if (arg instanceof WebData) { // unwrap the arg to a real value represented by the web data arg = ((WebData) arg).unwrap(); } if (arg instanceof byte[]) { byte[] bytes = (byte[]) arg; stmt.setBytes(i + 1, bytes); } else if (arg instanceof UUID) { UUID uuid = (UUID) arg; byte[] bytes = Msc.uuidToBytes(uuid); stmt.setBytes(i + 1, bytes); } else if (arg instanceof TUUID) { TUUID tuuid = (TUUID) arg; stmt.setBytes(i + 1, tuuid.toBytes()); } else { stmt.setObject(i + 1, arg); } } } public static <T> List<T> rows(Mapper<ResultSet, T> resultMapper, ResultSet rs) throws Exception { List<T> rows = U.list(); while (rs.next()) { rows.add(resultMapper.map(rs)); } return rows; } public static <T> List<T> rows(Class<T> resultType, ResultSet rs) throws Exception { List<T> rows = U.list(); ResultSetMetaData meta = rs.getMetaData(); int columnsN = meta.getColumnCount(); Prop[] props = new Prop[columnsN]; for (int i = 0; i < props.length; i++) { String name = meta.getColumnLabel(i + 1); props[i] = Beany.property(resultType, name, false); } while (rs.next()) { T row = resultType.newInstance(); for (int i = 0; i < columnsN; i++) { if (props[i] != null) { Object value = rs.getObject(i + 1); // 1-indexed props[i].set(row, value); } } rows.add(row); } return rows; } private static Object convertResultValue(String type, Object value) { byte[] bytes; switch (type) { case "TUUID": U.must(value instanceof byte[], "Expecting byte[] value to convert to TUUID!"); bytes = (byte[]) value; return TUUID.fromBytes(bytes); case "UUID": U.must(value instanceof byte[], "Expecting byte[] value to convert to UUID!"); bytes = (byte[]) value; return Msc.bytesToUUID(bytes); default: throw U.rte("Unknown type: '%s'", type); } } public static List<Map<String, Object>> rows(ResultSet rs) throws SQLException { List<Map<String, Object>> rows = U.list(); while (rs.next()) { rows.add(row(rs)); } return rows; } public static Map<String, Object> row(ResultSet rs) throws SQLException { Map<String, Object> row = U.map(); ResultSetMetaData meta = rs.getMetaData(); int columnsNumber = meta.getColumnCount(); for (int i = 1; i <= columnsNumber; i++) { String name = meta.getColumnLabel(i); Object value = rs.getObject(i); String[] nameParts = name.split("__"); if (nameParts.length == 2) { name = nameParts[0]; value = convertResultValue(nameParts[1], value); } row.put(name, value); } return row; } }