/* * #%L * model * %% * Copyright (C) 2012 - 2016 valdasraps * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * <http://www.gnu.org/licenses/lgpl-3.0.html>. * #L% */ package lt.emasina.resthub.factory; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j; import lt.emasina.resthub.TableFactory; import lt.emasina.resthub.model.MdTable; import oracle.jdbc.OracleConnection; @Log4j @RequiredArgsConstructor public abstract class SqlTableFactory extends TableFactory { /** * MdTable + Metadata hashcode */ private final Map<MdTable, Integer> tableCodes = new HashMap<>(); public abstract String getTablesSql(); public abstract String getConnectionName(); public abstract MdTable getMdTable(ResultSet rs) throws SQLException; protected void applyParameters(PreparedStatement ps) throws SQLException { } @Override public boolean isRefresh() { boolean refresh = false; try (OracleConnection conn = getCf().getConnection(getConnectionName())) { Map<MdTable, Integer> tables= new HashMap<>(); tables.putAll(tableCodes); tableCodes.clear(); for (MdTable t: this.getTablesInternal()) { Integer hc = 0; try { hc = getMdTableHashcode(conn, t); } catch (SQLException ex) { if (log.isDebugEnabled()) { log.debug(String.format("Error while reading metadata for %s", t), ex); } } if (tables.containsKey(t)) { if (!Objects.equals(tables.get(t), hc)) { refresh = true; } tables.remove(t); } else { refresh = true; } tableCodes.put(t, hc); } if (!tables.isEmpty()) { refresh = true; } } catch (SQLException ex) { log.error("Error while connecting to DB", ex); } return refresh; } private Integer getMdTableHashcode(OracleConnection conn, MdTable t) throws SQLException { StringBuilder sb = new StringBuilder(); try (PreparedStatement ps = conn.prepareStatement(t.getSql())) { ResultSetMetaData md = ps.getMetaData(); for (int c = 1; c <= md.getColumnCount(); c++) { sb.append(md.getColumnName(c)) .append(" ") .append(md.getColumnTypeName(c)) .append("(") .append(md.getScale(c)) .append(",") .append(md.getPrecision(c)) .append(");"); } } return sb.toString().hashCode(); } private List<MdTable> getTablesInternal() throws SQLException { List<MdTable> tables = new ArrayList<>(); try (OracleConnection conn = getCf().getConnection(getConnectionName())) { try (PreparedStatement ps = conn.prepareStatement(getTablesSql())) { applyParameters(ps); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { tables.add(getMdTable(rs)); } } } } return tables; } @Override public List<MdTable> getTables() throws Exception { return getTablesInternal(); } @Override public void close() throws Exception { tableCodes.clear(); } }