JPA 标准中具有复合主键的 IN 子句

2023-11-22

我有一个名为group_tableMySQL 中只有两列user_group_id and group_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_id and group_id分别。

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


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

In this scenario, the query returns the same list as it is supplied from the database which is needless in reality. I actually need this query for deletion of multiple rows.


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

/** *****************************************************************************
 * 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 <[email protected]> - 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的开始 and //补丁n结束我会尝试向上游提交,但这可能需要一些时间

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JPA 标准中具有复合主键的 IN 子句 的相关文章

随机推荐

  • 如何在 Swift 中禁用 TextField 中的粘贴?

    我有一个TextField with a numberPad并且该函数仅在包含数字时运行 如果用户将字母粘贴到TextField并单击 确定 如何禁用粘贴TextField 我同意莱昂纳多 萨维奥 达布斯 如果我是你 我会使用字符串检查并发
  • C++ 有标准的编译时类型容器吗?

    这个问题有一个答案Nim其中提到升压 mpl 映射 标准 C 中是否有可以保存类型的编译时容器 一个用法示例是 compiler vector foo char short long long long template
  • 为什么从 float 转换为 double 会改变值?

    我一直试图找出原因 但找不到 有谁能够帮助我 看下面的例子 float f 125 32f System out println value of f f double d double 125 32f System out println
  • wkhtmltopdf google 地图输出的 JavaScript 延迟

    我正在与 WKTHMTOPDF 合作并且非常享受它 然而 正在转换的页面有谷歌地图 生成的 PDF 中的地图显示为半加载状态 我知道有一个选项可以添加 javascript delay在以前的版本中 但它似乎已被弃用 我使用的是0 99版本
  • 哪些音频格式(扩展)可以通过 just_audio Flutter 包在 ios 和 Android 上使用?

    哪些音频格式 扩展 可以通过 just audio Flutter 包在 ios 和 Android 上使用 想知道我应该允许用户使用 file picker 上传哪些扩展 以便他们可以使用 just audio 包 我在任何地方都找不到列
  • ASP.NET MVC 中动态更改主模板

    我需要在我的应用程序 ASP NET MVC 上支持不同的母版页 推荐的方法是什么 将母版页名称传递给视图 存储母版页 在会话中或其他内容中 以便在用户访问期间保留它 使用自定义基本控制器并继承它 Public Class CustomBa
  • jquery - 如何确定 div 是否更改其高度或任何 css 属性?

    当 div 更改其高度或任何 css 属性时 如何触发事件 我有一个 id 的 divmainContent 我希望jquery在改变高度时自动触发一个事件 我做了这样的事情 mainContent change height functi
  • 如何强制 Composer 使用 https:// 而不是 git://?

    我有这样的东西 repositories type package package name myrepo version dev master source url https github com me myrepo git type
  • 将 UITextView / UITextField 中的空格编码为 URL 格式

    我正在尝试将 UITextView 或 UITextField 的内容作为参数发送到 php 文件 NSString urlstr NSString alloc initWithFormat http server com file php
  • Javascript中如何检查字符是否为字母?

    我正在使用以下命令提取 Javascript 字符串中的字符 var first str charAt 0 我想检查一下这是否是一封信 奇怪的是 Javascript 中似乎不存在这样的功能 至少我找不到 我该如何测试这个 我不相信有一个内
  • DialogFragment 参数和空指针异常

    我的类应该向 DialogFragment 传递一个参数 但我的应用程序在 对话框类的 onCreate 方法中因 NullPointerException 而崩溃 对话框片段类部分代码 public class ConfirmDialog
  • 为什么使用匿名类型而不是创建类[重复]

    这个问题在这里已经有答案了 我正在重构一个旧的应用程序 它使用动态内联 SQL 从大型 Oracle 数据库中提取数据 我创建了一个运行良好的存储过程 PL SQL 由于它只有一行 数据行 我让它返回一个数据行 该类驻留在 DAL 中 当我
  • Edge 浏览器中的 focus()

    我在用popup focus 单击按钮后聚焦弹出窗口 这focus 除 EDGE 浏览器外 所有浏览器都可以正常工作 我面临的问题是间歇性的 有时我可以在浏览器上查看弹出窗口 子弹出对话框 有时可以在浏览器后面 即桌面上 查看 并且我可以通
  • 如何使用闹钟类设置闹钟

    您好 我正在尝试使用 AlarmClock 类在我的应用程序中设置闹钟 我使用的代码如下 Intent intent new Intent intent setAction AlarmClock ACTION SET ALARM start
  • 创建新的 Microsoft.CodeAnalysis.CustomWorkspace - 出现 ReflectionTypeLoadException

    我尝试在 NET 编译器平台 Roslyn 示例中创建类似 ConsoleClassifier 的内容 Microsoft CodeAnalysis v0 7 此时我得到了ReflectionTypeLoadException Custom
  • 从 URL 加载时应如何处理视网膜/普通图像?

    我了解如何以编程方式从 URL 为我的应用程序加载图像 而不是将它们打包在应用程序中 但如何处理 1x 与 2x 问题 如果需要 我可以从外部源提供这两个版本 但是在设置 UIImage 时如何处理 我很确定您无法以自动方式远程加载 2x
  • 使用 Node.js 作为简单的 Web 服务器

    我想运行一个非常简单的 HTTP 服务器 每个 GET 请求example com应得index html提供给它 但作为常规 HTML 页面 即 与阅读普通网页时的体验相同 使用下面的代码 我可以读取内容index html 我如何服务i
  • 带有片段或活动的 Android Studio 导航抽屉

    我正在尝试使用 Android Studio 的导航抽屉模板开发一个应用程序 因此 我使用此模板创建了一个新项目 但是当我运行程序并单击菜单项时 视图不会改变 我在互联网上到处搜索 但我不知道如何处理这个问题 这是Android Studi
  • libxslt 是否具有将一个文档拆分为多个文档的功能?

    看起来 libxslt 不支持 XSLT 2 0 并且xsl result document 有没有办法模仿xsl result document using libxslt or xsltproc 是的 有 使用exsl 文档 一个简单的
  • JPA 标准中具有复合主键的 IN 子句

    我有一个名为group tableMySQL 中只有两列user group id and group id 它们都是类型VARCHAR 这两列一起形成一个复合主键 我需要使用子选择执行语句IN 根据传递给查询的值列表选择行 Overrid