Author: bleny Date: 2013-06-25 15:23:08 +0200 (Tue, 25 Jun 2013) New Revision: 678 Url: http://nuiton.org/projects/sandbox/repository/revisions/678 Log: improve OracleCompliantImprovedNamingStrategy by preventing conflicts Modified: nuiton-jpa/nuiton-jpa-hibernate/src/main/java/org/nuiton/jpa/hibernate/OracleCompliantImprovedNamingStrategy.java nuiton-jpa/nuiton-jpa-hibernate/src/test/java/org/nuiton/jpa/hibernate/OracleCompliantImprovedNamingStrategyTest.java Modified: nuiton-jpa/nuiton-jpa-hibernate/src/main/java/org/nuiton/jpa/hibernate/OracleCompliantImprovedNamingStrategy.java =================================================================== --- nuiton-jpa/nuiton-jpa-hibernate/src/main/java/org/nuiton/jpa/hibernate/OracleCompliantImprovedNamingStrategy.java 2013-06-24 21:16:58 UTC (rev 677) +++ nuiton-jpa/nuiton-jpa-hibernate/src/main/java/org/nuiton/jpa/hibernate/OracleCompliantImprovedNamingStrategy.java 2013-06-25 13:23:08 UTC (rev 678) @@ -2,15 +2,16 @@ import com.google.common.base.Joiner; import com.google.common.base.Splitter; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import org.apache.commons.lang3.StringUtils; +import org.hibernate.HibernateException; import org.hibernate.cfg.ImprovedNamingStrategy; import org.hibernate.cfg.NamingStrategy; import java.util.List; -import java.util.Map; /** * Oracle doesn't support table names and columns names longer than 30 characters. @@ -34,7 +35,7 @@ */ protected static final int MAXIMUM_IDENTIFIER_LENGTH = 30; - protected Map<String, String> cache = Maps.newConcurrentMap(); + protected BiMap<String, String> identifierToShortenedIdentifier = HashBiMap.create(); /** * Given an identifier, return a similar identifier but no longer @@ -52,15 +53,27 @@ protected String getShortenedIdentifier(String value) { String reducedValue = value; if (value.length() > MAXIMUM_IDENTIFIER_LENGTH) { - reducedValue = cache.get(value); + reducedValue = identifierToShortenedIdentifier.get(value); if (reducedValue == null) { reducedValue = shortenIdentifier(value); - cache.put(value, reducedValue); + checkConflict(value, reducedValue); + identifierToShortenedIdentifier.put(value, reducedValue); } } return reducedValue; } + protected void checkConflict(String value, String reducedValue) { + boolean conflictInShortenedIdentifier = identifierToShortenedIdentifier.inverse().containsKey(reducedValue); + if (conflictInShortenedIdentifier) { + String otherIdentifier = identifierToShortenedIdentifier.inverse().get(reducedValue); + throw new HibernateException( + "error while using this naming strategy, a conflict occurred when short identifier for '" + + value + "' was computed. Generated short identifier (" + reducedValue + + ") is already used for identifier " + otherIdentifier); + } + } + protected String shortenIdentifier(String identifier) { Iterable <String> split = Splitter.on('_').split(identifier); int numberOfElements = Iterables.size(split); Modified: nuiton-jpa/nuiton-jpa-hibernate/src/test/java/org/nuiton/jpa/hibernate/OracleCompliantImprovedNamingStrategyTest.java =================================================================== --- nuiton-jpa/nuiton-jpa-hibernate/src/test/java/org/nuiton/jpa/hibernate/OracleCompliantImprovedNamingStrategyTest.java 2013-06-24 21:16:58 UTC (rev 677) +++ nuiton-jpa/nuiton-jpa-hibernate/src/test/java/org/nuiton/jpa/hibernate/OracleCompliantImprovedNamingStrategyTest.java 2013-06-25 13:23:08 UTC (rev 678) @@ -1,6 +1,8 @@ package org.nuiton.jpa.hibernate; +import com.google.common.base.Preconditions; import org.apache.commons.lang3.StringUtils; +import org.hibernate.HibernateException; import org.junit.Assert; import org.junit.Test; @@ -79,4 +81,30 @@ return StringUtils.rightPad(value, 50, "LONG"); } + @Test + public void testConflictsRaiseException() { + + String longClassName1 = getLongValue("LONG_CLASS_NAME_") + "1"; + String longClassName2 = getLongValue("LONG_CLASS_NAME_") + "2"; + + // to lead to a conflict, we must check that both long class names + // will have the same reduced identifier + String shortenedIdentifier = oracleCompliantImprovedNamingStrategy.shortenIdentifier(longClassName1); + Preconditions.checkState( + shortenedIdentifier.equals(oracleCompliantImprovedNamingStrategy.shortenIdentifier(longClassName2)) + ); + + // now check that an exception is raised and exception message is explicit + oracleCompliantImprovedNamingStrategy.classToTableName(longClassName1); + + try { + oracleCompliantImprovedNamingStrategy.classToTableName(longClassName2); + Assert.fail("an exception should have been raised"); + } catch (HibernateException e) { + Assert.assertTrue(StringUtils.containsIgnoreCase(e.getMessage(), longClassName1)); + Assert.assertTrue(StringUtils.containsIgnoreCase(e.getMessage(), longClassName2)); + Assert.assertTrue(StringUtils.containsIgnoreCase(e.getMessage(), shortenedIdentifier)); + } + + } }