hibernate - 在 JPA 条件中具有复合主键的 IN 子句

标签 hibernate jpa eclipselink composite-primary-key jpa-2.1

我有一张名为 group_table 的表在只有两列的 MySQL 中 user_group_idgroup_id (它们都是 VARCHAR 类型)。这两个列共同构成一个复合主键。

我需要使用子选择执行语句 IN()根据传递给查询的值列表选择行。

@Override
@SuppressWarnings("unchecked")
public List<GroupTable> getList(List<GroupTable> list)
{
    CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
    CriteriaQuery<GroupTable> criteriaQuery=criteriaBuilder.createQuery(GroupTable.class);
    Root<GroupTable> root = criteriaQuery.from(entityManager.getMetamodel().entity(GroupTable.class));
    criteriaQuery.where(root.in(list));
    return entityManager.createQuery(criteriaQuery).getResultList();
}

该实现产生以下查询。

SELECT group_id, 
       user_group_id 
FROM   projectdb.group_table 
WHERE  ((?, ?) IN ((?, ?), (?, ?))) 

/*Binding parameters.*/
bind => [null, null, ROLE_AAA, aaa, ROLE_BBB, aaa]

请注意,与组合键本身有关的前两个参数是 null .他们应该是 user_group_idgroup_id分别。

为什么它们在参数列表中没有被替换?

虽然我对在表中形成复合主键不感兴趣,但这对于我用于身份验证的 JAAS 来说(可能)是强制性的。

在这种情况下,查询返回与从数据库提供的列表相同的列表,这实际上是不必要的。我实际上需要这个查询来删除多行。

最佳答案

这是 eclipse 链接中缺少的功能。我为此开发了一个补丁

/** *****************************************************************************
 * Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
 * which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 * Oracle - initial API and implementation from Oracle TopLink
 * Nicolas Marcotte <nicolas.marcotte@usherbrooke.ca> - patch for IN on composite keys comming from expression builder 

 ***************************************************************************** */
package org.eclipse.persistence.internal.expressions;

import java.io.*;
import java.util.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.persistence.internal.helper.*;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.queries.*;
import org.eclipse.persistence.exceptions.*;
import org.eclipse.persistence.expressions.*;
import org.eclipse.persistence.internal.databaseaccess.*;
import org.eclipse.persistence.internal.sessions.AbstractRecord;

/**
 * <p>
 * <b>Purpose</b>: Expression SQL printer.
 * <p>
 * <b>Responsibilities</b>:<ul>
 * <li> Print an expression in SQL format.
 * <li> Replaces FIELD types with field names from the descriptor.
 * <li> Replaces PARAMETER types with row or object values.
 * <li> Calls accessor to print primitive types.
 * </ul>
 * <p>
 * @author Dorin Sandu
 * @since TOPLink/Java 1.0
 */
public class ExpressionSQLPrinter {

    /**
     * Stores the current session. The session accessor
     * is used to print all the primitive types.
     */
    protected AbstractSession session;

    /**
     * Stores the current platform to access platform specific functions.
     */
    protected DatabasePlatform platform;

    /**
     * Stores the call being created.
     */
    protected SQLCall call;

    /**
     * Stores the row. Used to print PARAMETER nodes.
     */
    protected AbstractRecord translationRow;

    /**
     * Indicates whether fully qualified field names
     * (owner + table) should be used or not.
     */
    protected boolean shouldPrintQualifiedNames;

    // What we write on
    protected Writer writer;

    /** Used for distincts in functions. */
    protected boolean requiresDistinct;

    // Used in figuring out when to print a comma in the select line
    protected boolean isFirstElementPrinted;
    private final ExpressionBuilder builder;

    public ExpressionSQLPrinter(AbstractSession session, AbstractRecord translationRow, SQLCall call, boolean printQualifiedNames, ExpressionBuilder builder) {
        this.session = session;
        this.translationRow = translationRow;
        this.call = call;
        this.shouldPrintQualifiedNames = printQualifiedNames;
        // reference session's platform directly if builder or builder's descriptor is null
        if (builder == null || builder.getDescriptor() == null) {
            this.platform = getSession().getPlatform();
        } else {
            this.platform = (DatabasePlatform) getSession().getPlatform(builder.getDescriptor().getJavaClass());
        }
        this.requiresDistinct = false;
        this.builder = builder;
        isFirstElementPrinted = false;
    }

    /**
     * Return the call.
     */
    public SQLCall getCall() {
        return call;
    }

    /**
     * INTERNAL:
     * Return the database platform specific information.
     */
    public DatabasePlatform getPlatform() {
        return this.platform;
    }

    protected AbstractSession getSession() {
        return session;
    }

    /**
     * INTERNAL:
     * Return the row for translation
     */
    protected AbstractRecord getTranslationRow() {
        return translationRow;
    }

    public Writer getWriter() {
        return writer;
    }

    /**
     * INTERNAL:
     * Used in figuring out when to print a comma in the select clause
     */
    public boolean isFirstElementPrinted() {
        return isFirstElementPrinted;
    }

    public void printExpression(Expression expression) {
        translateExpression(expression);
    }

    public void printField(DatabaseField field) {
        if (field == null) {
            return;
        }
        //start of patch 1
        //resolve alias if is was not already done 
        if (builder.getTableAliases() != null) {
            DatabaseTable keyAtValue = builder.getTableAliases().keyAtValue(field.getTable());
            if (keyAtValue != null) {
                field.setTableName(keyAtValue.getName());
            }
        }
         //end of patch 1
        try {
            // Print the field using either short or long notation i.e. owner + table name.
            if (shouldPrintQualifiedNames()) {
                getWriter().write(field.getQualifiedNameDelimited(platform));
            } else {
                getWriter().write(field.getNameDelimited(platform));
            }
        } catch (IOException exception) {
            throw ValidationException.fileError(exception);
        }
    }

    public void printParameter(ParameterExpression expression) {
        try {
            final Logger logger = LogManager.getLogger();

            getCall().appendTranslationParameter(getWriter(), expression, getPlatform(), getTranslationRow());

        } catch (IOException exception) {
            throw ValidationException.fileError(exception);            
        }
    }

    public void printParameter(DatabaseField field) {
        getCall().appendTranslation(getWriter(), field);
    }

    public void printPrimitive(Object value) {
        if (value instanceof Collection) {
            printValuelist((Collection) value);
            return;
        }

        session.getPlatform().appendLiteralToCall(getCall(), getWriter(), value);
    }

    public void printNull(ConstantExpression nullValueExpression) {
        if (session.getPlatform().shouldBindLiterals()) {
            DatabaseField field = null;
            Expression localBase = nullValueExpression.getLocalBase();
            if (localBase.isFieldExpression()) {
                field = ((FieldExpression) localBase).getField();
            } else if (localBase.isQueryKeyExpression()) {
                field = ((QueryKeyExpression) localBase).getField();
            }
            session.getPlatform().appendLiteralToCall(getCall(), getWriter(), field);
        } else {
            session.getPlatform().appendLiteralToCall(getCall(), getWriter(), null);
        }
    }

    public void printString(String value) {
        try {
            getWriter().write(value);

        } catch (IOException exception) {
            throw ValidationException.fileError(exception);
        }
    }

    public void printValuelist(Collection values) {
        try {
            getWriter().write("(");
            Iterator valuesEnum = values.iterator();
            while (valuesEnum.hasNext()) {
                Object value = valuesEnum.next();
                // Support nested arrays for IN.
                if (value instanceof Collection) {
                    printValuelist((Collection) value);
                } else if (value instanceof Expression) {
                    ((Expression) value).printSQL(this);
                //start of patch 2
                } else if (value instanceof DatabaseField) {

                    printExpression(builder.getField((DatabaseField) value));            
                //end of patch 2
                } else {
                    session.getPlatform().appendLiteralToCall(getCall(), getWriter(), value);
                }
                if (valuesEnum.hasNext()) {
                    getWriter().write(", ");
                }
            }
            getWriter().write(")");
        } catch (IOException exception) {
            throw ValidationException.fileError(exception);
        }
    }

    /*
     * Same as printValuelist, but allows for collections containing expressions recursively
     */
    public void printList(Collection values) {
        try {
            getWriter().write("(");
            Iterator valuesEnum = values.iterator();
            while (valuesEnum.hasNext()) {
                Object value = valuesEnum.next();
                if (value instanceof Expression) {
                    ((Expression) value).printSQL(this);
                } else {
                    session.getPlatform().appendLiteralToCall(getCall(), getWriter(), value);
                }
                if (valuesEnum.hasNext()) {
                    getWriter().write(", ");
                }                
            }
            getWriter().write(")");
        } catch (IOException exception) {
            throw ValidationException.fileError(exception);
        }
    }

    /**
     * If a distinct has been set the DISTINCT clause will be printed.
     * This is required for batch reading.
     */
    public boolean requiresDistinct() {
        return requiresDistinct;
    }

    protected void setCall(SQLCall call) {
        this.call = call;
    }

    /**
     * INTERNAL:
     * Used in figuring out when to print a comma in the select clause
     */
    public void setIsFirstElementPrinted(boolean isFirstElementPrinted) {
        this.isFirstElementPrinted = isFirstElementPrinted;
    }

    /**
     * If a distinct has been set the DISTINCT clause will be printed.
     * This is required for batch reading.
     */
    public void setRequiresDistinct(boolean requiresDistinct) {
        this.requiresDistinct = requiresDistinct;
    }

    protected void setSession(AbstractSession theSession) {
        session = theSession;
    }

    protected void setShouldPrintQualifiedNames(boolean shouldPrintQualifiedNames) {
        this.shouldPrintQualifiedNames = shouldPrintQualifiedNames;
    }

    /**
     * INTERNAL:
     * Set the row for translation
     */
    protected void setTranslationRow(AbstractRecord theRow) {
        translationRow = theRow;
    }

    public void setWriter(Writer writer) {
        this.writer = writer;
    }

    public boolean shouldPrintParameterValues() {
        return getTranslationRow() != null;
    }

    protected boolean shouldPrintQualifiedNames() {
        return shouldPrintQualifiedNames;
    }

    /**
     * Translate an expression i.e. call the appropriate
     * translation method for the expression based on its
     * type. The translation method is then responsible
     * for translating the subexpressions.
     */
    protected void translateExpression(Expression theExpression) {
        theExpression.printSQL(this);
    }
}

补丁由 分隔//补丁n的开始//补丁n结束
我会尝试向上游提交它,但这可能需要时间

关于hibernate - 在 JPA 条件中具有复合主键的 IN 子句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24109412/

相关文章:

java - Spring、JPA 和 hibernate 的问题

java - Mysql 复杂对象 Hibernate 批量插入

java - 带有 boolean 字段的 Spring JPA 抛出 "cannot resolve property exception"

java - 线程 "main"javax.persistence.RollbackException : Transaction marked as rollbackOnly 中出现异常

java - Hibernate 4.3.0.Final 和 Spring Data JPA 1.4.3.RELEASE

java - 如何在JPA中显式指定 'inverse' @OneToMany关系

mysql - 与同一实体类型和表的多个一对多关系?

mysql - 如何更改原始数据类型的默认值

eclipse - 在 eclipse 的 toplink 中显示生成的 SQL

java - ClassLoader getResources 未找到文件的所有版本