001package fr.ifremer.adagio.core.dao.technical; 002 003/* 004 * #%L 005 * Tutti :: Persistence API 006 * $Id: TuttiEntities.java 1578 2014-02-07 15:31:18Z tchemit $ 007 * $HeadURL: http://svn.forge.codelutin.com/svn/tutti/trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities/TuttiEntities.java $ 008 * %% 009 * Copyright (C) 2012 Ifremer 010 * %% 011 * This program is free software: you can redistribute it and/or modify 012 * it under the terms of the GNU Affero General Public License as published by 013 * the Free Software Foundation, either version 3 of the License, or 014 * (at your option) any later version. 015 * 016 * This program is distributed in the hope that it will be useful, 017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 019 * GNU General Public License for more details. 020 * 021 * You should have received a copy of the GNU Affero General Public License 022 * along with this program. If not, see <http://www.gnu.org/licenses/>. 023 * #L% 024 */ 025 026import java.io.File; 027import java.math.BigDecimal; 028import java.math.MathContext; 029import java.sql.Connection; 030import java.sql.DriverManager; 031import java.sql.ResultSet; 032import java.sql.SQLException; 033import java.sql.Statement; 034import java.text.DecimalFormat; 035import java.text.DecimalFormatSymbols; 036import java.util.Properties; 037 038import javax.sql.DataSource; 039 040import org.apache.commons.lang3.StringUtils; 041import org.apache.commons.logging.Log; 042import org.apache.commons.logging.LogFactory; 043import org.hibernate.Query; 044import org.hibernate.Session; 045import org.hibernate.cfg.AvailableSettings; 046import org.hibernate.cfg.Environment; 047import org.springframework.dao.DataAccessResourceFailureException; 048import org.springframework.dao.DataIntegrityViolationException; 049import org.springframework.dao.DataRetrievalFailureException; 050import org.springframework.jdbc.datasource.DataSourceUtils; 051 052import com.google.common.base.Preconditions; 053 054import fr.ifremer.adagio.core.config.AdagioConfiguration; 055 056/** 057 * Usefull method around DAO and entities. 058 * 059 * @author tchemit <chemit@codelutin.com> 060 * @author Benoit Lavenier <benoit.lavenier@e-is.pro> 061 * @since 3.5 062 */ 063public class DaoUtils { 064 065 private final static String JDBC_URL_PREFIX_HSQLDB = "jdbc:hsqldb:file:"; 066 067 /** Logger. */ 068 private static final Log log = LogFactory.getLog(DaoUtils.class); 069 070 protected DaoUtils() { 071 // helper class does not instanciate 072 } 073 074 /** 075 * Create a new hibernate configuration, with all hbm.xml files 076 * for the schema need for app 077 * 078 * @return the hibernate Configuration 079 */ 080 public static Properties getConnectionProperties(String jdbcUrl, String username, String password, String schema, String dialect, String driver) { 081 082 // Building a new configuration 083 Properties p = new Properties(); 084 085 // Set driver 086 p.setProperty(Environment.DRIVER, driver); 087 088 // Set hibernate dialect 089 p.setProperty(Environment.DIALECT, dialect); 090 091 // To be able to retrieve connection 092 p.setProperty(Environment.URL, jdbcUrl); 093 p.setProperty(Environment.USER, username); 094 p.setProperty(Environment.PASS, password); 095 096 if (StringUtils.isNotBlank(schema)) { 097 p.setProperty(Environment.DEFAULT_SCHEMA, schema); 098 } 099 100 // Try with synonyms enable 101 p.setProperty(AvailableSettings.ENABLE_SYNONYMS, "true"); 102 103 // Pour tester avec le metadata generic (normalement plus long pour Oracle) 104 // cfg.setProperty("hibernatetool.metadatadialect", "org.hibernate.cfg.rveng.dialect.JDBCMetaDataDialect"); 105 if (jdbcUrl.startsWith("jdbc:oracle")) { 106 p.setProperty("hibernatetool.metadatadialect", "org.hibernate.cfg.rveng.dialect.OracleMetaDataDialect"); 107 } 108 109 return p; 110 } 111 112 public static void closeSilently(Statement statement) { 113 try { 114 if (statement != null && !statement.isClosed()) { 115 statement.close(); 116 } 117 } catch (AbstractMethodError e) { 118 try { 119 statement.close(); 120 } catch (SQLException e1) { 121 } 122 if (log.isDebugEnabled()) { 123 log.debug("Fix this linkage error, damned hsqlsb 1.8.0.7:("); 124 } 125 } catch (IllegalAccessError e) { 126 if (log.isDebugEnabled()) { 127 log.debug("Fix this IllegalAccessError error, damned hsqlsb 1.8.0.7:("); 128 } 129 } catch (Exception e) { 130 if (log.isErrorEnabled()) { 131 log.error("Could not close statement, but do not care", e); 132 } 133 } 134 } 135 136 public static void closeSilently(Connection connection) { 137 try { 138 if (connection != null && !connection.isClosed()) { 139 connection.close(); 140 } 141 } catch (Exception e) { 142 if (log.isErrorEnabled()) { 143 log.error("Could not close connection, but do not care", e); 144 } 145 } 146 } 147 148 public static void closeSilently(ResultSet statement) { 149 try { 150 if (statement != null && !statement.isClosed()) { 151 152 statement.close(); 153 } 154 } catch (AbstractMethodError e) { 155 try { 156 statement.close(); 157 } catch (SQLException e1) { 158 } 159 if (log.isDebugEnabled()) { 160 log.debug("Fix this linkage error, damned hsqlsb 1.8.0.7:("); 161 } 162 } catch (IllegalAccessError e) { 163 if (log.isDebugEnabled()) { 164 log.debug("Fix this IllegalAccessError error, damned hsqlsb 1.8.0.7:("); 165 } 166 } catch (Exception e) { 167 if (log.isErrorEnabled()) { 168 log.error("Could not close statement, but do not care", e); 169 } 170 } 171 } 172 173 public static void closeSilently(Session session) { 174 try { 175 if (session != null && session.isOpen()) { 176 177 session.close(); 178 } 179 } catch (Exception e) { 180 if (log.isErrorEnabled()) { 181 log.error("Could not close session, but do not care", e); 182 } 183 } 184 } 185 186 public static Connection createConnection(Properties connectionProperties) throws SQLException { 187 return createConnection( 188 connectionProperties.getProperty(Environment.URL), 189 connectionProperties.getProperty(Environment.USER), 190 connectionProperties.getProperty(Environment.PASS) 191 ); 192 } 193 194 public static String getUrl(Properties connectionProperties) { 195 return connectionProperties.getProperty(Environment.URL); 196 } 197 198 public static Connection createConnection(String jdbcUrl, 199 String user, 200 String password) throws SQLException { 201 Connection connection = DriverManager.getConnection(jdbcUrl, 202 user, 203 password); 204 connection.setAutoCommit(false); 205 return connection; 206 } 207 208 public static void fillConnectionProperties(Properties p, 209 String url, 210 String username, 211 String password) { 212 p.put(Environment.URL, url); 213 p.put(Environment.USER, username); 214 p.put(Environment.PASS, password); 215 } 216 217 public static String getJdbcUrl(File directory, String dbName) { 218 String jdbcUrl = JDBC_URL_PREFIX_HSQLDB + directory.getAbsolutePath() + "/" + dbName; 219 jdbcUrl = jdbcUrl.replaceAll("\\\\", "/"); 220 return jdbcUrl; 221 } 222 223 public static boolean isFileDatabase(String jdbcUrl) { 224 Preconditions.checkNotNull(jdbcUrl); 225 return jdbcUrl.startsWith(JDBC_URL_PREFIX_HSQLDB); 226 } 227 228 /** 229 * Check if connection properties are valid. Try to open a SQL connection, then close it. 230 * If no error occur, the connection is valid. 231 * @param jdbcDriver 232 * @param jdbcUrl 233 * @param user 234 * @param password 235 * @return 236 */ 237 public static boolean isValidConnectionProperties( 238 String jdbcDriver, 239 String jdbcUrl, 240 String user, 241 String password) { 242 String driverClassName = jdbcDriver; 243 try { 244 Class<?> driverClass = Class.forName(driverClassName); 245 DriverManager.registerDriver((java.sql.Driver)driverClass.newInstance()); 246 } catch (Exception e) { 247 log.error("Could not load JDBC Driver: " + e.getMessage(), e); 248 return false; 249 } 250 251 Connection connection = null; 252 try { 253 connection = createConnection( 254 jdbcUrl, 255 user, 256 password); 257 return true; 258 } catch (SQLException e) { 259 log.error("Could not connect to database: " + e.getMessage().trim()); 260 } finally { 261 DaoUtils.closeSilently(connection); 262 } 263 return false; 264 } 265 266 267 private static final double EARTH_RADIUS = 6378288.0; 268 269 private static final MathContext MATH_CONTEXT_4_DIGIT = new MathContext(4); 270 271 private static DecimalFormatSymbols symbols; 272 273 private static DecimalFormat decimalFormat; 274 275 public static int computeDistanceInMeters(Float startLatitude, 276 Float startLongitude, 277 Float endLatitude, 278 Float endLongitude) { 279 280 double sLat = startLatitude * Math.PI / 180.0; 281 double sLong = startLongitude * Math.PI / 180.0; 282 double eLat = endLatitude * Math.PI / 180.0; 283 double eLong = endLongitude * Math.PI / 180.0; 284 285 Double d = EARTH_RADIUS * 286 (Math.PI / 2 - Math.asin(Math.sin(eLat) * Math.sin(sLat) 287 + Math.cos(eLong - sLong) * Math.cos(eLat) * Math.cos(sLat))); 288 return d.intValue(); 289 } 290 291 public static String getDistanceInMilles(Float distance) { 292 String distanceText; 293 if (distance != null) { 294 Float distanceInMilles = distance / 1852; 295 distanceText = String.format("%.3f", distanceInMilles); 296 297 } else { 298 distanceText = ""; 299 } 300 return distanceText; 301 } 302 303 public static float getRoundedLengthStep(float lengthStep, boolean aroundUp) { 304 int intValue = (int) ((lengthStep + (aroundUp ? 0.001f : 0f)) * 10); 305 float result = intValue / 10f; 306 return result; 307 } 308 309 public static DecimalFormatSymbols getDecimalFormatSymbols() { 310 if (symbols == null) { 311 symbols = new DecimalFormatSymbols(); 312 symbols.setDecimalSeparator('.'); 313 symbols.setGroupingSeparator(' '); 314 } 315 return symbols; 316 } 317 318 public static DecimalFormat getDecimalFormat(int minDecimal, int maxDecimal) { 319 if (decimalFormat == null) { 320 decimalFormat = new DecimalFormat(); 321 decimalFormat.setDecimalFormatSymbols(getDecimalFormatSymbols()); 322 decimalFormat.setGroupingUsed(false); 323 } 324 decimalFormat.setMinimumFractionDigits(minDecimal); 325 decimalFormat.setMaximumFractionDigits(maxDecimal); 326 return decimalFormat; 327 } 328 329 public static String getWeightStringValue(Float weight) { 330 String textValue; 331 if (weight != null) { 332 DecimalFormat weightDecimalFormat = getDecimalFormat(1, 3); 333 textValue = weightDecimalFormat.format(weight); 334 335 } else { 336 textValue = ""; 337 } 338 return textValue; 339 } 340 341 public static <N extends Number> N getValueOrComputedValue(N value, N computedValue) { 342 return value == null ? computedValue : value; 343 } 344 345 public static <N extends Number> Boolean getValueOrComputedValueComputed(N value, N computedValue) { 346 Boolean result; 347 if (value == null) { 348 349 result = computedValue == null ? null : true; 350 } else { 351 result = false; 352 } 353 return result; 354 } 355 356 /** 357 * Round the given value to max 4 digits. 358 * 359 * @param value the float to round. 360 * @return the rounded value 361 * @since 1.0.1 362 */ 363 public static float roundKiloGram(float value) { 364 BigDecimal sumB = new BigDecimal(value); 365 float result = sumB.abs(MATH_CONTEXT_4_DIGIT).floatValue(); 366 return result; 367 } 368 369 /** 370 * Compare two weights with rounding them to kilograms. 371 * 372 * @param v0 first weight to compare 373 * @param v1 second weight to compare 374 * @return 1 if v0 > v1, -1 if v0 < v1, 0 if v0 == v1 375 */ 376 public static int compareWeights(float v0, float v1) { 377 v0 = roundKiloGram(v0); 378 v1 = roundKiloGram(v1); 379 float delta = v0 - v1; 380 int result; 381 if (delta > 0.00001) { 382 // v0 > v1 383 result = 1; 384 } else if (delta < -0.0001f) { 385 // v0 < v1 386 result = -1; 387 } else { 388 // v0 == v1 389 result = 0; 390 } 391 return result; 392 } 393 394 public static boolean isSmallerWeight(float v0, float v1) { 395 return compareWeights(v0, v1) < 0; 396 } 397 398 public static boolean isGreaterWeight(float v0, float v1) { 399 return compareWeights(v0, v1) > 0; 400 } 401 402 public static boolean isEqualWeight(float v0, float v1) { 403 return compareWeights(v0, v1) == 0; 404 } 405 406 public static boolean isNotEqualWeight(float v0, float v1) { 407 return compareWeights(v0, v1) != 0; 408 } 409 410 /** 411 * set parameter values for a query: 'statusValidCode' and 'statusTemporaryCode' 412 * @param query a query with this parameters inside 413 * @return the given query object 414 */ 415 public static Query withStatus(Query query) { 416 query.setString("statusValidCode", AdagioConfiguration.getInstance().getStatusCodeValid()); 417 query.setString("statusTemporaryCode",AdagioConfiguration.getInstance().getStatusCodeTemporary()); 418 return query; 419 } 420 421 public static Double convertToDouble(Float floatValue) { 422 if (floatValue == null) return null; 423 // TODO : trouver une meilleur solution (attention ŕ ne pas perdre de précision) 424 return Double.parseDouble(Float.toString(floatValue)); 425 } 426 427 public static Float convertToFloat(Double doubleValue) { 428 if (doubleValue == null) return null; 429 // TODO : trouver une meilleur solution (attention ŕ ne pas perdre de précision) 430 return Float.parseFloat(Double.toString(doubleValue)); 431 } 432 433 public static int sqlUpdate(DataSource dataSource, String sql) { 434 Connection connection = DataSourceUtils.getConnection(dataSource); 435 try { 436 return sqlUpdate(connection, sql); 437 } 438 finally { 439 DataSourceUtils.releaseConnection(connection, dataSource); 440 } 441 } 442 443 public static int sqlUpdate(Connection connection, String sql) { 444 Statement stmt = null; 445 try { 446 stmt = connection.createStatement(); 447 } catch (SQLException ex) { 448 closeSilently(stmt); 449 throw new DataAccessResourceFailureException("Could not open database connection", ex); 450 } 451 452 // Log using a special logger 453 if (log.isDebugEnabled()) { 454 log.debug(sql); 455 } 456 457 try { 458 return stmt.executeUpdate(sql); 459 } catch (SQLException ex) { 460 throw new DataIntegrityViolationException("Could not execute query: " + sql, ex); 461 } 462 finally { 463 closeSilently(stmt); 464 } 465 } 466 467 public static Object sqlUnique(DataSource dataSource, String sql) { 468 Connection connection = DataSourceUtils.getConnection(dataSource); 469 try { 470 return sqlUnique(connection, sql); 471 } 472 finally { 473 DataSourceUtils.releaseConnection(connection, dataSource); 474 } 475 } 476 477 @SuppressWarnings("unchecked") 478 public static <T extends Object> T sqlUniqueTyped(DataSource dataSource, String sql) { 479 return (T)sqlUnique(dataSource, sql); 480 } 481 482 public static Object sqlUnique(Connection connection, String sql) { 483 Statement stmt = null; 484 try { 485 stmt = connection.createStatement(); 486 } catch (SQLException ex) { 487 closeSilently(stmt); 488 throw new DataAccessResourceFailureException("Could not open database connection", ex); 489 } 490 491 // Log using a special logger 492 if (log.isDebugEnabled()) { 493 log.debug(sql); 494 } 495 496 try { 497 ResultSet rs = stmt.executeQuery(sql); 498 if (!rs.next()) { 499 throw new DataRetrievalFailureException("Executed query return no row: " + sql); 500 } 501 Object result = rs.getObject(1); 502 if (rs.next()) { 503 throw new DataRetrievalFailureException("Executed query has more than one row: " + sql); 504 } 505 return result; 506 507 } catch (SQLException ex) { 508 throw new DataIntegrityViolationException("Could not execute query: " + sql, ex); 509 } 510 finally { 511 closeSilently(stmt); 512 } 513 } 514 515 @SuppressWarnings("unchecked") 516 public static <T extends Object> T sqlUniqueTyped(Connection connection, String sql) { 517 return (T)sqlUnique(connection, sql); 518 } 519 520 public static void shutdownDatabase(Connection connection) { 521 try { 522 String jdbcUrl = connection.getMetaData().getURL(); 523 if (jdbcUrl.startsWith(JDBC_URL_PREFIX_HSQLDB)) { 524 sqlUpdate(connection, "SHUTDOWN"); 525 } 526 } catch (SQLException e) { 527 e.printStackTrace(); 528 } 529 } 530 531 public static void shutdownDatabase(Properties connectionProperties) throws SQLException { 532 Connection conn = DaoUtils.createConnection(connectionProperties); 533 try { 534 shutdownDatabase(conn); 535 } finally { 536 closeSilently(conn); 537 } 538 } 539} 540 541