View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.waarp.openr66.database.data;
22  
23  import com.fasterxml.jackson.databind.JsonNode;
24  import com.fasterxml.jackson.databind.node.ObjectNode;
25  import org.waarp.common.database.DbPreparedStatement;
26  import org.waarp.common.database.data.AbstractDbData;
27  import org.waarp.common.database.data.DbValue;
28  import org.waarp.common.database.exception.WaarpDatabaseException;
29  import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
30  import org.waarp.common.database.exception.WaarpDatabaseNoDataException;
31  import org.waarp.common.database.exception.WaarpDatabaseSqlException;
32  import org.waarp.common.database.model.DbModelAbstract;
33  import org.waarp.common.json.JsonHandler;
34  import org.waarp.common.logging.WaarpLogger;
35  import org.waarp.common.logging.WaarpLoggerFactory;
36  import org.waarp.openr66.dao.AbstractDAO;
37  import org.waarp.openr66.dao.DAOFactory;
38  import org.waarp.openr66.dao.exception.DAOConnectionException;
39  import org.waarp.openr66.dao.exception.DAONoDataException;
40  
41  import java.sql.Types;
42  import java.util.Iterator;
43  import java.util.Map.Entry;
44  
45  /**
46   * Abstract database table implementation
47   */
48  public abstract class AbstractDbDataDao<E> extends AbstractDbData {
49    private static final WaarpLogger logger =
50        WaarpLoggerFactory.getLogger(AbstractDbDataDao.class);
51    public static final String JSON_MODEL = "@model";
52    protected static final String SHOULD_NOT_BE_CALLED = "Should not be called";
53  
54    protected E pojo;
55  
56    /**
57     * Abstract constructor
58     */
59    protected AbstractDbDataDao() {
60      // nothing
61    }
62  
63    /**
64     * Validate Byte array max length
65     *
66     * @param values the values to check against Types.VARBINARY
67     *
68     * @throws WaarpDatabaseSqlException if length is not acceptable
69     */
70    public static void validateLength(final byte[]... values)
71        throws WaarpDatabaseSqlException {
72      for (final byte[] value : values) {
73        if (value != null && value.length > DbModelAbstract.MAX_BINARY * 2) {
74          throw new WaarpDatabaseSqlException(
75              "BINARY value exceed max size: " + value.length + " (" +
76              DbModelAbstract.MAX_BINARY + ")");
77        }
78      }
79    }
80  
81    /**
82     * Validate String max length
83     *
84     * @param type between Types.VARCHAR, NVARCHAR, LONGVARCHAR
85     * @param values the values to check against same type
86     *
87     * @throws WaarpDatabaseSqlException if length is not acceptable
88     */
89    public static void validateLength(final int type, final String... values)
90        throws WaarpDatabaseSqlException {
91      for (final String value : values) {
92        if (value == null) {
93          continue;
94        }
95        switch (type) {
96          case Types.VARCHAR:
97            if (value.length() > DbModelAbstract.MAX_VARCHAR) {
98              throw new WaarpDatabaseSqlException(
99                  "VARCHAR value exceed max size: " + value.length() + " (" +
100                 DbModelAbstract.MAX_VARCHAR + ")");
101           }
102           break;
103         case Types.NVARCHAR:
104           if (value.length() > DbModelAbstract.MAX_KEY_VARCHAR) {
105             throw new WaarpDatabaseSqlException(
106                 "VARCHAR as KEY value exceed max size: " + value.length() +
107                 " (" + DbModelAbstract.MAX_KEY_VARCHAR + ")");
108           }
109           break;
110         case Types.LONGVARCHAR:
111           if (value.length() > DbModelAbstract.MAX_LONGVARCHAR) {
112             throw new WaarpDatabaseSqlException(
113                 "LONGVARCHAR value exceed max size: " + value.length() + " (" +
114                 DbModelAbstract.MAX_LONGVARCHAR + ")");
115           }
116           break;
117         default:
118           break;
119       }
120     }
121   }
122 
123   protected abstract String getTable();
124 
125   protected abstract void checkValues() throws WaarpDatabaseSqlException;
126 
127   protected abstract AbstractDAO<E> getDao(final boolean isCacheable)
128       throws DAOConnectionException;
129 
130   protected abstract String getPrimaryKey();
131 
132   protected abstract String getPrimaryField();
133 
134   /**
135    * Change UpdatedInfo status
136    *
137    * @param info
138    */
139   public abstract void changeUpdatedInfo(AbstractDbData.UpdatedInfo info);
140 
141   /**
142    * Test the existence of the current object
143    *
144    * @return True if the object exists
145    *
146    * @throws WaarpDatabaseException
147    */
148   @Override
149   public boolean exist() throws WaarpDatabaseException {
150     AbstractDAO<E> abstractDAO = null;
151     try {
152       abstractDAO = getDao(true);
153       return abstractDAO.exist(getPrimaryKey());
154     } catch (final DAOConnectionException e) {
155       throw new WaarpDatabaseNoConnectionException(e);
156     } finally {
157       DAOFactory.closeDAO(abstractDAO);
158     }
159   }
160 
161   /**
162    * Select object from table
163    *
164    * @throws WaarpDatabaseException
165    */
166   @Override
167   public void select() throws WaarpDatabaseException {
168     AbstractDAO<E> abstractDAO = null;
169     try {
170       abstractDAO = getDao(true);
171       pojo = abstractDAO.select(getPrimaryKey());
172       isSaved = true;
173     } catch (final DAOConnectionException e) {
174       throw new WaarpDatabaseNoConnectionException(e);
175     } catch (final DAONoDataException e) {
176       throw new WaarpDatabaseNoDataException((e));
177     } finally {
178       DAOFactory.closeDAO(abstractDAO);
179     }
180   }
181 
182   /**
183    * Insert object into table
184    *
185    * @throws WaarpDatabaseException
186    */
187   @Override
188   public void insert() throws WaarpDatabaseException {
189     if (isSaved) {
190       return;
191     }
192     checkValues();
193     AbstractDAO<E> abstractDAO = null;
194     try {
195       abstractDAO = getDao(false);
196       abstractDAO.insert(pojo);
197       isSaved = true;
198     } catch (final DAOConnectionException e) {
199       throw new WaarpDatabaseNoConnectionException(e);
200     } finally {
201       DAOFactory.closeDAO(abstractDAO);
202     }
203   }
204 
205   /**
206    * Update object to table
207    *
208    * @throws WaarpDatabaseException
209    */
210   @Override
211   public void update() throws WaarpDatabaseException {
212     if (isSaved) {
213       return;
214     }
215     checkValues();
216     AbstractDAO<E> abstractDAO = null;
217     try {
218       abstractDAO = getDao(false);
219       abstractDAO.update(pojo);
220       isSaved = true;
221     } catch (final DAOConnectionException e) {
222       throw new WaarpDatabaseNoConnectionException(e);
223     } catch (final DAONoDataException e) {
224       throw new WaarpDatabaseNoDataException((e));
225     } finally {
226       DAOFactory.closeDAO(abstractDAO);
227     }
228   }
229 
230   /**
231    * Delete object from table
232    *
233    * @throws WaarpDatabaseException
234    */
235   @Override
236   public void delete() throws WaarpDatabaseException {
237     AbstractDAO<E> abstractDAO = null;
238     try {
239       abstractDAO = getDao(false);
240       abstractDAO.delete(pojo);
241       isSaved = false;
242     } catch (final DAOConnectionException e) {
243       throw new WaarpDatabaseNoConnectionException(e);
244     } catch (final DAONoDataException e) {
245       throw new WaarpDatabaseNoDataException((e));
246     } finally {
247       DAOFactory.closeDAO(abstractDAO);
248     }
249   }
250 
251   /**
252    * @return the PoJo as Json
253    */
254   @Override
255   public final String asJson() {
256     final ObjectNode node = getJson();
257     return JsonHandler.writeAsString(node);
258   }
259 
260   /**
261    * @return the PoJo as Json (shall not be used except for native Json but
262    *     possibly incorrect
263    */
264   public String toJson() {
265     return JsonHandler.writeAsString(pojo);
266   }
267 
268   /**
269    * Create the equivalent object in Json (no database access)
270    *
271    * @return The ObjectNode Json equivalent
272    */
273   @Override
274   public ObjectNode getJson() {
275     final ObjectNode node = JsonHandler.createObjectNode();
276     node.put(JSON_MODEL, getClass().getSimpleName());
277     final String json = JsonHandler.writeAsString(pojo);
278     final ObjectNode subnode = JsonHandler.getFromString(json);
279     if (subnode != null) {
280       for (final Iterator<Entry<String, JsonNode>> it = subnode.fields();
281            it.hasNext(); ) {
282         final Entry<String, JsonNode> entry = it.next();
283         node.set(entry.getKey(), entry.getValue());
284       }
285     }
286     return node;
287   }
288 
289   protected abstract void setFromJson(String field, JsonNode value)
290       throws WaarpDatabaseSqlException;
291 
292   /**
293    * Set the values from the Json node to the current object (no database
294    * access)
295    *
296    * @param node
297    * @param ignorePrimaryKey True will ignore primaryKey from Json
298    *
299    * @throws WaarpDatabaseSqlException
300    */
301   @Override
302   public void setFromJson(final ObjectNode node, final boolean ignorePrimaryKey)
303       throws WaarpDatabaseSqlException {
304     boolean foundPrimaryKey = false;
305     for (final Iterator<Entry<String, JsonNode>> it = node.fields();
306          it.hasNext(); ) {
307       final Entry<String, JsonNode> entry = it.next();
308       logger.debug("{} = {}", entry.getKey(), entry.getValue());
309       if ("UPDATEDINFO".equalsIgnoreCase(entry.getKey())) {
310         continue;
311       }
312       if (getPrimaryField().equalsIgnoreCase(entry.getKey())) {
313         if (ignorePrimaryKey) {
314           continue;
315         } else {
316           foundPrimaryKey = !entry.getValue().isNull() &&
317                             !entry.getValue().asText().isEmpty();
318         }
319       }
320       setFromJson(entry.getKey(), entry.getValue());
321       isSaved = false;
322     }
323     checkValues();
324     if (!ignorePrimaryKey && foundPrimaryKey) {
325       try {
326         insert();
327       } catch (final WaarpDatabaseException e) {
328         try {
329           update();
330         } catch (final WaarpDatabaseException ex) {
331           logger.error("Cannot save item: {}", ex.getMessage());
332         }
333       }
334     }
335   }
336 
337   @Override
338   protected void initObject() {
339     // nothing
340   }
341 
342   /**
343    * {@link UnsupportedOperationException}
344    *
345    * @return never
346    */
347   @Override
348   protected String getWherePrimaryKey() {
349     throw new UnsupportedOperationException(SHOULD_NOT_BE_CALLED);
350   }
351 
352   /**
353    * {@link UnsupportedOperationException}
354    */
355   @Override
356   protected void setPrimaryKey() {
357     throw new UnsupportedOperationException(SHOULD_NOT_BE_CALLED);
358   }
359 
360   /**
361    * {@link UnsupportedOperationException}
362    *
363    * @return never
364    */
365   @Override
366   protected String getSelectAllFields() {
367     throw new UnsupportedOperationException(SHOULD_NOT_BE_CALLED);
368   }
369 
370   /**
371    * {@link UnsupportedOperationException}
372    *
373    * @return never
374    */
375   @Override
376   protected String getInsertAllValues() {
377     throw new UnsupportedOperationException(SHOULD_NOT_BE_CALLED);
378   }
379 
380   /**
381    * {@link UnsupportedOperationException}
382    *
383    * @return never
384    */
385   @Override
386   protected String getUpdateAllFields() {
387     throw new UnsupportedOperationException(SHOULD_NOT_BE_CALLED);
388   }
389 
390   /**
391    * {@link UnsupportedOperationException}
392    */
393   @Override
394   protected void setToArray() {
395     throw new UnsupportedOperationException(SHOULD_NOT_BE_CALLED);
396   }
397 
398   /**
399    * {@link UnsupportedOperationException}
400    */
401   @Override
402   protected void setFromArray() {
403     throw new UnsupportedOperationException(SHOULD_NOT_BE_CALLED);
404   }
405 
406   /**
407    * {@link UnsupportedOperationException}
408    */
409   @Override
410   protected void getValues(final DbPreparedStatement preparedStatement,
411                            final DbValue[] values) {
412     throw new UnsupportedOperationException(SHOULD_NOT_BE_CALLED);
413   }
414 }