Skip to content

Commit

Permalink
Fixed failing test and #179
Browse files Browse the repository at this point in the history
  • Loading branch information
beikov committed Dec 5, 2015
1 parent f7aa46d commit 2290692
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -686,17 +686,17 @@ public void testNotBetweenCharacter() {

@Test
public void testBetweenDate() {
GeneralCaseExpression result = (GeneralCaseExpression) parse("CASE WHEN a.x BETWEEN (d '1991-05-21') AND (d '1991-05-22') THEN 0 ELSE 1 END");
GeneralCaseExpression result = (GeneralCaseExpression) parse("CASE WHEN a.x BETWEEN {d '1991-05-21'} AND {d '1991-05-22'} THEN 0 ELSE 1 END");

GeneralCaseExpression expected = new GeneralCaseExpression(Arrays.asList(new WhenClauseExpression(new BetweenPredicate(path("a", "x"), foo("(d '1991-05-21')"), foo("(d '1991-05-22')")), foo("0"))), foo("1"));
GeneralCaseExpression expected = new GeneralCaseExpression(Arrays.asList(new WhenClauseExpression(new BetweenPredicate(path("a", "x"), foo("{d '1991-05-21'}"), foo("{d '1991-05-22'}")), foo("0"))), foo("1"));
assertEquals(expected, result);
}

@Test
public void testNotBetweenDate() {
GeneralCaseExpression result = (GeneralCaseExpression) parse("CASE WHEN a.x NOT BETWEEN (d '1991-05-21') AND (d '1991-05-22') THEN 0 ELSE 1 END");
GeneralCaseExpression result = (GeneralCaseExpression) parse("CASE WHEN a.x NOT BETWEEN {d '1991-05-21'} AND {d '1991-05-22'} THEN 0 ELSE 1 END");

GeneralCaseExpression expected = new GeneralCaseExpression(Arrays.asList(new WhenClauseExpression(new BetweenPredicate(path("a", "x"), foo("(d '1991-05-21')"), foo("(d '1991-05-22')"), true), foo("0"))), foo("1"));
GeneralCaseExpression expected = new GeneralCaseExpression(Arrays.asList(new WhenClauseExpression(new BetweenPredicate(path("a", "x"), foo("{d '1991-05-21'}"), foo("{d '1991-05-22'}"), true), foo("0"))), foo("1"));
assertEquals(expected, result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@
*/
package com.blazebit.persistence.impl.hibernate.function;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import javax.persistence.EntityManager;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.dialect.CUBRIDDialect;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.Dialect;
Expand Down Expand Up @@ -56,6 +62,34 @@
public class HibernateEntityManagerIntegrator implements EntityManagerIntegrator {

private static final Logger LOG = Logger.getLogger(EntityManagerIntegrator.class.getName());
private final Map<WeakCacheKey<SessionFactory>, Map<String, SQLFunction>> functionsCache = new ConcurrentHashMap<WeakCacheKey<SessionFactory>, Map<String,SQLFunction>>(1);

private static class WeakCacheKey<K> extends WeakReference<K> {

private final int hash;

public WeakCacheKey(K referent) {
super(referent);
this.hash = System.identityHashCode(referent); // compare by identity
}

@Override
public int hashCode() {
return hash;
}

@Override
public boolean equals(Object obj) {
K key;
return obj == this ||
obj != null &&
obj.getClass() == this.getClass() &&
// cleared CacheKey is only equal to itself
(key = this.get()) != null &&
// compare key by identity
key == ((WeakCacheKey<K>) obj).get();
}
}

@Override
public String getDbms(EntityManager entityManager) {
Expand Down Expand Up @@ -97,34 +131,137 @@ private String getDbmsName(Dialect dialect) {
@Override
public EntityManager registerFunctions(EntityManager em, Map<String, JpqlFunctionGroup> dbmsFunctions) {
Session s = em.unwrap(Session.class);
Map<String, SQLFunction> functions = getFunctions(s);
Dialect dialect = getDialect(s);
String dbms = getDbmsName(dialect);

for (Map.Entry<String, JpqlFunctionGroup> functionEntry : dbmsFunctions.entrySet()) {
String functionName = functionEntry.getKey();
JpqlFunctionGroup dbmsFunctionMap = functionEntry.getValue();
JpqlFunction function = dbmsFunctionMap.get(dbms);

if (function == null && !dbmsFunctionMap.contains(dbms)) {
function = dbmsFunctionMap.get(null);
}
if (function == null) {
LOG.warning("Could not register the function '" + functionName + "' because there is neither an implementation for the dbms '" + dbms + "' nor a default implementation!");
} else {
functions.put(functionName, new HibernateJpqlFunctionAdapter(function));
}
WeakCacheKey<SessionFactory> key = new WeakCacheKey<SessionFactory>(s.getSessionFactory());

// We already registered it once
if (functionsCache.get(key) != null) {
return em;
}

synchronized (s.getSessionFactory()) {
// Double check
if (functionsCache.get(key) != null) {
return em;
}

Map<String, SQLFunction> functions = getFunctions(s);
Dialect dialect = getDialect(s);
String dbms = getDbmsName(dialect);

for (Map.Entry<String, JpqlFunctionGroup> functionEntry : dbmsFunctions.entrySet()) {
String functionName = functionEntry.getKey();
JpqlFunctionGroup dbmsFunctionMap = functionEntry.getValue();
JpqlFunction function = dbmsFunctionMap.get(dbms);

if (function == null && !dbmsFunctionMap.contains(dbms)) {
function = dbmsFunctionMap.get(null);
}
if (function == null) {
LOG.warning("Could not register the function '" + functionName + "' because there is neither an implementation for the dbms '" + dbms + "' nor a default implementation!");
} else {
functions.put(functionName, new HibernateJpqlFunctionAdapter(function));
}
}

setFunctions(s, functions);
functionsCache.put(key, functions);
}

return em;
}

@Override
public Set<String> getRegisteredFunctions(EntityManager em) {
Session s = em.unwrap(Session.class);
WeakCacheKey<SessionFactory> key = new WeakCacheKey<SessionFactory>(s.getSessionFactory());

// We already registered it once
Map<String, SQLFunction> map = functionsCache.get(key);
if (map != null) {
return map.keySet();
}

return getFunctions(s).keySet();
}

private void setFunctions(Session s, Map<String, SQLFunction> functions) {
String version = s.getClass().getPackage().getImplementationVersion();

String[] versionParts = version.split("\\.");
int major = Integer.parseInt(versionParts[0]);
int minor = Integer.parseInt(versionParts[1]);
int fix = Integer.parseInt(versionParts[2]);
String type = versionParts[3];

if (major < 5 || (major == 5 && minor == 0 && fix == 0 && "Beta1".equals(type))) {
// Implementation detail: Hibernate uses a mutable map, so we can do this
Dialect d = getDialect(s);
Exception ex;
Field f = null;
boolean madeAccessible = true;

// We have to retrieve the functionMap the old fashioned way via reflection :(
try {
f = Dialect.class.getDeclaredField("sqlFunctions");
madeAccessible = !f.isAccessible();

if (madeAccessible) {
f.setAccessible(true);
}

f.set(d, functions);
return;
} catch (NoSuchFieldException e) {
ex = e;
} catch (IllegalArgumentException e) {
// This can never happen
ex = e;
} catch (IllegalAccessException e) {
ex = e;
} finally {
if (f != null && madeAccessible) {
f.setAccessible(false);
}
}

throw new RuntimeException("Could not update the function map to dynamically register functions. Please report this version of hibernate(" + version + ") so we can provide support for it!", ex);
} else {
SessionFactoryImplementor sf = (SessionFactoryImplementor) s.getSessionFactory();
SQLFunctionRegistry registry = sf.getSqlFunctionRegistry();

Exception ex;
Field f = null;
boolean madeAccessible = true;

// We have to retrieve the functionMap the old fashioned way via reflection :(
try {
f = SQLFunctionRegistry.class.getDeclaredField("functionMap");
madeAccessible = !f.isAccessible();

if (madeAccessible) {
f.setAccessible(true);
}

f.set(registry, functions);
return;
} catch (NoSuchFieldException e) {
ex = e;
} catch (IllegalArgumentException e) {
// This can never happen
ex = e;
} catch (IllegalAccessException e) {
ex = e;
} finally {
if (f != null && madeAccessible) {
f.setAccessible(false);
}
}

throw new RuntimeException("Could not update the function map to dynamically register functions. Please report this version of hibernate(" + version + ") so we can provide support for it!", ex);
}
}

@SuppressWarnings("unchecked")
private Map<String, SQLFunction> getFunctions(Session s) {
String version = s.getClass().getPackage().getImplementationVersion();
Expand All @@ -137,24 +274,35 @@ private Map<String, SQLFunction> getFunctions(Session s) {

if (major < 5 || (major == 5 && minor == 0 && fix == 0 && "Beta1".equals(type))) {
// Implementation detail: Hibernate uses a mutable map, so we can do this
return getDialect(s).getFunctions();
return new HashMap<String, SQLFunction>(getDialect(s).getFunctions());
} else {
SessionFactoryImplementor sf = (SessionFactoryImplementor) s.getSessionFactory();
SQLFunctionRegistry registry = sf.getSqlFunctionRegistry();
Exception ex;
Field f = null;
boolean madeAccessible = true;

// We have to retrieve the functionMap the old fashioned way via reflection :(
try {
Field f = SQLFunctionRegistry.class.getDeclaredField("functionMap");
f.setAccessible(true);
return (Map<String, SQLFunction>) f.get(registry);
f = SQLFunctionRegistry.class.getDeclaredField("functionMap");
madeAccessible = !f.isAccessible();

if (madeAccessible) {
f.setAccessible(true);
}

return new TreeMap<String, SQLFunction>((SortedMap<String, SQLFunction>) f.get(registry));
} catch (NoSuchFieldException e) {
ex = e;
} catch (IllegalArgumentException e) {
// This can never happen
ex = e;
} catch (IllegalAccessException e) {
ex = e;
} finally {
if (f != null && madeAccessible) {
f.setAccessible(false);
}
}

throw new RuntimeException("Could not access the function map to dynamically register functions. Please report this version of hibernate(" + version + ") so we can provide support for it!", ex);
Expand Down

0 comments on commit 2290692

Please sign in to comment.