Ich hatte ja in meiner Bachelorarbeit die Umstellung einer Persistenzschicht zum Thema. Es sollte von JDO nach JPA gehen. Dabei habe ich viele Persistenzframeworks miteinander verglichen und mich damals für EclipseLink entschieden.
Jetzt wo meine Bachelorarbeit von 2010 umgesetzt wird, kommen zum Teil Probleme zum Vorschein, die in meiner kleinen Testumgebung damals noch nicht wirklich sichtbar waren. Eines dieser Probleme ist die Limitierung eine SQL-Abfrage mit IN unter Oracle. Es sind nur 1000 Argumente erlaubt und es traten in der Software ringsum auch mal größere Argumentlisten auf, die der Datenbankserver regelmäßig mit ORA-01795 quitierte.
Um dies zu umgehen wurde schon das alte Persistenzframework ein wenig angepasst. Also kam mir die Aufgabe zu, das nun auch mit EclipseLink zu tun. Nach einiger Recherche und der Nachfrage auf der Mailingliste habe ich dazu mal einen Bugeintrag eingestellt. Meine vorläufige Umgehungsstrategie möchte ich euch natürlich nicht vorenthalten.
/** * Passt den SQL String an, wenn zum beispiel eine IN-Funktion * * @param query Die auszuführende Query * @param wrapper Wrapper (da wir hier statisch sind) oder {@code null} wenn wir aus der {@link DBVerbindung} kommen * @param em Der Entitymanager in dem das ganze stattfinden soll * */ static void preProcessQuery(Query query, EntityManager em, AbstractQueryWrapper< ? > wrapper) { Session session = em.unwrap(JpaEntityManager.class).getActiveSession(); DatabaseQuery databaseQuery = ((EJBQueryImpl< ? >) query).getDatabaseQuery(); DatabaseRecord record; if (wrapper == null) { record = new DatabaseRecord(); } else { record = wrapper.createDBRecord(); } String sqlString = databaseQuery.getTranslatedSQLString(session, record); if (sqlString.contains(" IN ")) { StringBuilder sqlBuilder = new StringBuilder(); String[] inTokens = sqlString.split(" IN "); // Der Teil vor dem ersten IN boolean not = inTokens[0].endsWith("NOT"); sqlBuilder.append(inTokens[0]); // Der Spaltenname nach dem gesucht wird String descriptor = inTokens[0].substring(inTokens[0].indexOf('(') + 1); descriptor = descriptor.split(" ")[0]; for (int i = 1; i < inTokens.length; i++) { // Der Teil in der Klammer String inArgument = inTokens[i]; if (i > 1) { not = inTokens[i - 1].endsWith("NOT"); descriptor = inTokens[i - 1].substring(inTokens[i - 1].indexOf('(') + 1, inTokens[i - 1].lastIndexOf(" NOT")); } StringTokenizer argumentTokenizer = new StringTokenizer(inArgument, ","); if (argumentTokenizer.countTokens() < 1000) { sqlBuilder.append(" IN "); sqlBuilder.append(inArgument); } else { sqlBuilder.append(" IN "); int numArguments = argumentTokenizer.countTokens(); for (int j = 1; j < numArguments; j++) { sqlBuilder.append(argumentTokenizer.nextToken()); // Die magische ORA-Grenze if (j % 1000 == 0) { sqlBuilder.append(')'); if (not) { sqlBuilder.append(" AND "); sqlBuilder.append(descriptor); sqlBuilder.append(" NOT IN("); } else { sqlBuilder.append(" OR"); sqlBuilder.append(descriptor); sqlBuilder.append(" IN("); } } else { sqlBuilder.append(','); } } sqlBuilder.deleteCharAt(sqlBuilder.length() - 1); sqlBuilder.append("))"); } } ReadAllQuery readAllQuery = query.unwrap(ReadAllQuery.class); readAllQuery.setSQLString(sqlBuilder.toString()); readAllQuery.setShouldPrepare(true); } }