001/* 002 * #%L 003 * Netarchivesuite - harvester 004 * %% 005 * Copyright (C) 2005 - 2018 The Royal Danish Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023 024package dk.netarkivet.harvester.datamodel.extendedfield; 025 026import java.sql.Connection; 027import java.sql.PreparedStatement; 028import java.sql.ResultSet; 029import java.sql.SQLException; 030import java.util.LinkedList; 031import java.util.List; 032 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import dk.netarkivet.common.exceptions.ArgumentNotValid; 037import dk.netarkivet.common.exceptions.IOFailure; 038import dk.netarkivet.common.exceptions.UnknownID; 039import dk.netarkivet.common.utils.DBUtils; 040import dk.netarkivet.harvester.datamodel.HarvestDBConnection; 041import dk.netarkivet.harvester.datamodel.HarvesterDatabaseTables; 042 043/** 044 * A database-based implementation of the ExtendedFieldDBDAO class. 045 */ 046public class ExtendedFieldDBDAO extends ExtendedFieldDAO { 047 048 /** The logger for this class. */ 049 private static final Logger log = LoggerFactory.getLogger(ExtendedFieldDBDAO.class); 050 051 /** 052 * Constructor for the ExtendedFieldDBDAO object. 053 */ 054 public ExtendedFieldDBDAO() { 055 Connection connection = HarvestDBConnection.get(); 056 try { 057 HarvesterDatabaseTables.checkVersion(connection, HarvesterDatabaseTables.EXTENDEDFIELD); 058 HarvesterDatabaseTables.checkVersion(connection, HarvesterDatabaseTables.EXTENDEDFIELDVALUE); 059 } finally { 060 HarvestDBConnection.release(connection); 061 } 062 } 063 064 @Override 065 public synchronized void create(ExtendedField aExtendedField) { 066 ArgumentNotValid.checkNotNull(aExtendedField, "aExtendedField"); 067 068 Connection connection = HarvestDBConnection.get(); 069 if (aExtendedField.getExtendedFieldID() != null) { 070 log.warn("The extendedFieldID for this extended Field is already set. This should probably never happen."); 071 } else { 072 aExtendedField.setExtendedFieldID(generateNextID(connection)); 073 } 074 075 log.debug("Creating {}", aExtendedField.toString()); 076 077 PreparedStatement statement = null; 078 try { 079 connection.setAutoCommit(false); 080 statement = connection.prepareStatement("" + "INSERT INTO extendedfield " 081 + " (extendedfield_id, " + " extendedfieldtype_id, " + " name, " 082 + " format, " + " defaultvalue, " + " options, " 083 + " datatype, " + " mandatory, " + " sequencenr, " 084 + " maxlen) " + "VALUES (?, " + " ?, " + " ?, " 085 + " ?, " + " ?, " + " ?, " + " ?, " 086 + " ?, " + " ?, " + " ?) "); 087 088 statement.setLong(1, aExtendedField.getExtendedFieldID()); 089 statement.setLong(2, aExtendedField.getExtendedFieldTypeID()); 090 statement.setString(3, aExtendedField.getName()); 091 statement.setString(4, aExtendedField.getFormattingPattern()); 092 statement.setString(5, aExtendedField.getDefaultValue()); 093 statement.setString(6, aExtendedField.getOptions()); 094 statement.setInt(7, aExtendedField.getDatatype()); 095 // the following conversion from boolean to int is necessary, 096 // because the database column 'mandatory' is a integer field 097 // and not a boolean (NAS-2127) 098 statement.setInt(8, aExtendedField.isMandatory() ? 1 : 0); 099 statement.setInt(9, aExtendedField.getSequencenr()); 100 statement.setInt(10, aExtendedField.getMaxlen()); 101 102 // TODO replace this toString by something better 103 log.debug(statement.toString()); 104 105 statement.executeUpdate(); 106 connection.commit(); 107 } catch (SQLException e) { 108 String message = "SQL error creating extended field " + aExtendedField + " in database" + "\n"; 109 log.warn(message, e); 110 throw new IOFailure(message, e); 111 } finally { 112 DBUtils.closeStatementIfOpen(statement); 113 DBUtils.rollbackIfNeeded(connection, "create extended field", aExtendedField); 114 HarvestDBConnection.release(connection); 115 } 116 } 117 118 /** 119 * Generates the next id of a extended field. this implementation retrieves the maximum value of extendedfield_id in 120 * the DB, and returns this value + 1. 121 * 122 * @param c an open connection to the HarvestDatabase 123 * @return The next available ID 124 */ 125 private Long generateNextID(Connection c) { 126 // FIXME Synchronization problem, this is why one should always use identity rows or generators. 127 Long maxVal = DBUtils.selectLongValue(c, "SELECT max(extendedfield_id) FROM extendedfield"); 128 if (maxVal == null) { 129 maxVal = 0L; 130 } 131 return maxVal + 1L; 132 } 133 134 /** 135 * Check whether a particular extended Field exists. 136 * 137 * @param aExtendedfieldId Id of the extended field. 138 * @return true if the extended field exists. 139 */ 140 public boolean exists(Long aExtendedfieldId) { 141 ArgumentNotValid.checkNotNull(aExtendedfieldId, "Long aExtendedfieldId"); 142 143 Connection c = HarvestDBConnection.get(); 144 try { 145 return exists(c, aExtendedfieldId); 146 } finally { 147 HarvestDBConnection.release(c); 148 } 149 } 150 151 /** 152 * Check, if there exists an ExtendedField with a given ID. 153 * 154 * @param c An open connection to the HarvestDatabase 155 * @param aExtendedfieldId An Id for a given Extended Field. 156 * @return true, if the extended field with the Id exists; otherwise false 157 */ 158 private synchronized boolean exists(Connection c, Long aExtendedfieldId) { 159 return 1 == DBUtils.selectLongValue(c, "SELECT COUNT(*) FROM extendedfield WHERE extendedfield_id = ?", 160 aExtendedfieldId); 161 } 162 163 @Override 164 public synchronized void update(ExtendedField aExtendedField) { 165 ArgumentNotValid.checkNotNull(aExtendedField, "aExtendedField"); 166 167 Connection connection = HarvestDBConnection.get(); 168 169 PreparedStatement statement = null; 170 try { 171 final Long extendedfieldId = aExtendedField.getExtendedFieldID(); 172 if (!exists(connection, extendedfieldId)) { 173 throw new UnknownID("Extended Field id " + extendedfieldId + " is not known in persistent storage"); 174 } 175 176 connection.setAutoCommit(false); 177 178 statement = connection.prepareStatement("" + "UPDATE extendedfield " + "SET extendedfield_id = ?, " 179 + " extendedfieldtype_id = ?, " + " name = ?, " + " format = ?, " 180 + " defaultvalue = ?, " + " options = ?, " + " datatype = ?, " 181 + " mandatory = ?, " + " sequencenr = ?, " + " maxlen = ? " 182 + "WHERE extendedfield_id = ? "); 183 184 statement.setLong(1, aExtendedField.getExtendedFieldID()); 185 statement.setLong(2, aExtendedField.getExtendedFieldTypeID()); 186 statement.setString(3, aExtendedField.getName()); 187 statement.setString(4, aExtendedField.getFormattingPattern()); 188 statement.setString(5, aExtendedField.getDefaultValue()); 189 statement.setString(6, aExtendedField.getOptions()); 190 statement.setInt(7, aExtendedField.getDatatype()); 191 // the following conversion from boolean to int is necessary, 192 // because the database column 'mandatory' is a int field 193 // and not a boolean (NAS-2127) 194 statement.setInt(8, aExtendedField.isMandatory() ? 1 : 0); 195 statement.setInt(9, aExtendedField.getSequencenr()); 196 statement.setInt(10, aExtendedField.getMaxlen()); 197 statement.setLong(11, aExtendedField.getExtendedFieldID()); 198 199 // TODO replace this toString by something better 200 log.debug(statement.toString()); 201 202 statement.executeUpdate(); 203 204 connection.commit(); 205 } catch (SQLException e) { 206 String message = "SQL error updating extendedfield " + aExtendedField + " in database" + "\n"; 207 log.warn(message, e); 208 throw new IOFailure(message, e); 209 } finally { 210 DBUtils.closeStatementIfOpen(statement); 211 DBUtils.rollbackIfNeeded(connection, "update extendedfield", aExtendedField); 212 HarvestDBConnection.release(connection); 213 } 214 } 215 216 @Override 217 public synchronized ExtendedField read(Long aExtendedfieldId) { 218 ArgumentNotValid.checkNotNull(aExtendedfieldId, "aExtendedfieldId"); 219 Connection connection = HarvestDBConnection.get(); 220 try { 221 return read(connection, aExtendedfieldId); 222 } finally { 223 HarvestDBConnection.release(connection); 224 } 225 } 226 227 /** 228 * Read an ExtendedField from database. 229 * 230 * @param connection A connection to the harvestDatabase 231 * @param aExtendedfieldId The ID for a given ExtendedField 232 * @return An ExtendedField object for the given ID. 233 */ 234 private synchronized ExtendedField read(Connection connection, Long aExtendedfieldId) { 235 if (!exists(connection, aExtendedfieldId)) { 236 throw new UnknownID("Extended Field id " + aExtendedfieldId + " is not known in persistent storage"); 237 } 238 239 ExtendedField extendedField = null; 240 PreparedStatement statement = null; 241 try { 242 statement = connection.prepareStatement("" + "SELECT extendedfieldtype_id, " + " name, " 243 + " format, " + " defaultvalue, " + " options, " + " datatype, " 244 + " mandatory, " + " sequencenr, " + " maxlen " + "FROM extendedfield " 245 + "WHERE extendedfield_id = ? "); 246 247 statement.setLong(1, aExtendedfieldId); 248 ResultSet result = statement.executeQuery(); 249 result.next(); 250 251 long extendedfieldtypeId = result.getLong(1); 252 String name = result.getString(2); 253 String format = result.getString(3); 254 String defaultvalue = result.getString(4); 255 String options = result.getString(5); 256 int datatype = result.getInt(6); 257 boolean mandatory = result.getInt(7) != 0; 258 int sequencenr = result.getInt(8); 259 int maxlen = result.getInt(9); 260 261 extendedField = new ExtendedField(aExtendedfieldId, extendedfieldtypeId, name, format, datatype, mandatory, 262 sequencenr, defaultvalue, options, maxlen); 263 264 return extendedField; 265 } catch (SQLException e) { 266 String message = "SQL error reading extended Field " + aExtendedfieldId + " in database" + "\n"; 267 log.warn(message, e); 268 throw new IOFailure(message, e); 269 } 270 } 271 272 @Override 273 public List<ExtendedField> getAll(long aExtendedFieldTypeId) { 274 Connection c = HarvestDBConnection.get(); 275 try { 276 List<Long> idList = DBUtils.selectLongList(c, "SELECT extendedfield_id FROM extendedfield " 277 + "WHERE extendedfieldtype_id = ? ORDER BY sequencenr ASC", aExtendedFieldTypeId); 278 List<ExtendedField> extendedFields = new LinkedList<ExtendedField>(); 279 for (Long extendedfieldId : idList) { 280 extendedFields.add(read(c, extendedfieldId)); 281 } 282 return extendedFields; 283 } finally { 284 HarvestDBConnection.release(c); 285 } 286 } 287 288 @Override 289 public void delete(long aExtendedfieldId) throws IOFailure { 290 ArgumentNotValid.checkNotNull(aExtendedfieldId, "aExtendedfieldId"); 291 292 Connection c = HarvestDBConnection.get(); 293 PreparedStatement stm = null; 294 try { 295 c.setAutoCommit(false); 296 297 stm = c.prepareStatement("DELETE FROM extendedfieldvalue WHERE extendedfield_id = ?"); 298 stm.setLong(1, aExtendedfieldId); 299 stm.executeUpdate(); 300 stm.close(); 301 stm = c.prepareStatement("DELETE FROM extendedfield WHERE extendedfield_id = ?"); 302 stm.setLong(1, aExtendedfieldId); 303 stm.executeUpdate(); 304 c.commit(); 305 } catch (SQLException e) { 306 String message = "SQL error deleting extended fields for ID " + aExtendedfieldId + "\n"; 307 log.warn(message, e); 308 } finally { 309 DBUtils.closeStatementIfOpen(stm); 310 DBUtils.rollbackIfNeeded(c, "delete extended field", aExtendedfieldId); 311 HarvestDBConnection.release(c); 312 } 313 } 314 315}