1

我必须处理 Java 来创建一个宁静的 API,并且我将它与 MySQL 一起使用来查询数据库。

我对Java很陌生,这可能是一个非常基本的问题,但习惯于不存在这个问题的PHP框架,我想知道在Java中做这件事的方法是什么。

我正在查询通过外键连接它们的 3 个表,我发现如果其中两个表具有相同的列名,则只会返回其中一个。语句中声明的最新表中的那个from。似乎他们得到了覆盖。

例如,如果表type_applicationtype_leaves都有一个name列,则在此查询中只返回name来自的列:type_leaves

select * from leaves, type_applications, type_leaves 
    where leaves.type_leave = type_leaves.id and 
    leaves.type_application = type_applications.id;

现在,我知道这可以通过指定所有需要的以表名为前缀的列名(以防它们重复)并as用于创建别名来轻松解决:

select leaves.id, type_leaves.name as leaves_name, type_applications.name as application_name
    from leaves, type_applications, type_leaves 
    where leaves.type_leave = type_leaves.id and 
    leaves.type_application = type_applications.id;

但这听起来对我来说不是最好的解决方案。我宁愿继续使用*以获取所有字段(这是我一直想要的)。

如果我随着时间不断添加或删除表中的列,这也将有助于获得更小的查询、更易于阅读和更易于维护。

有什么解决办法吗?在Java中处理它的方法是什么?

4

5 回答 5

4

我也是 Java 新手,所以我听到了;)。但在我尽力给你答案之前,如果你不介意的话,我想说一句。您的问题与 Java 和数据库/SQL 有关。您提供了一些 SQL,它解释了有关您的数据库表的一些事情以及您可以从查询中得到什么。但是您的主要问题是 Java 并且您没有提供任何 Java 代码,以便我们可以更好地了解您要完成的工作以及您遇到问题的具体位置。话虽如此,我希望以下内容能让您了解您可以做什么:

首先,ResultSet您的查询中包含两name列。它们不会被覆盖。这是一个例子:

String sql = "select * from leaves, type_applications, type_leaves " +
                "where leaves.type_leave = type_leaves.id and " +
                "leaves.type_application = type_applications.id";

ResultSet rs = stmt.executeQuery(sql);
DBTablePrinter.printResultSet(rs);

这将打印如下内容:

Printing 1 rows from table(s) LEAVES, TYPE_APPLICATIONS, TYPE_LEAVES
+----+------------------------+------------+------------------+----+--------+
| ID | SOMETHING_ABOUT_LEAVES | ID |        NAME        | ID |     NAME     |
+----+------------------------+----+--------------------+----+--------------+
|  1 | green                  |  1 | application type 1 |  1 | leave type 1 |
+----+------------------------+----+--------------------+----+--------------+

可以看出,两name列都在那里。(我使用了我编写的 DBTablePrinter 实用程序类。如果您有兴趣,可以在这里找到它)。正如 Strawberry 在评论中所做的那样,我还建议考虑使用显式连接语法。

我正在使用其他数据库(H2),所以我不确定这是否适用于 MySQL,但您可以尝试(它对我有用):

// After executing the query
rs.next();
System.out.println(rs.getString("TYPE_LEAVES.NAME"));
System.out.println(rs.getString("TYPE_APPLICATIONS.NAME"));

// Prints:
// leave type 1
// application type 1

如果这不起作用,或者您确实需要为列名添加前缀并使用这些新名称访问它们,那么我能想到的就是这样的:

ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();

// A HashMap with column names as key
Map<String, List<String>> columns = new HashMap<>(columnCount);

// Loop through columns, rename as you wish
for (int i = 1; i <= columnCount; i++) {
    if (rsmd.getColumnLabel(i).equals("NAME")) {
        if (rsmd.getTableName(i).equals("TYPE_LEAVES")) {
            columns.put("L_NAME", new ArrayList<>());
        } else {
            columns.put("APP_NAME", new ArrayList<>());
        }
    } else {
        columns.put(rsmd.getColumnLabel(i), new ArrayList<>());
    }
}

// Iterate over ResultSet rows, add values to columns
while (rs.next()) {
    for (int i = 1; i <= columnCount; i++) {
        String columnName = rsmd.getColumnLabel(i);
        String tableName = rsmd.getTableName(i);
        if (columnName.equals("NAME")) {
            if (tableName.equals("TYPE_LEAVES")) {
                columns.get("L_NAME").add(rs.getString(i));
            } else {
                columns.get("APP_NAME").add(rs.getString(i));
            }
        } else {
            columns.get(columnName).add(rs.getString(i))
        }
    }
}

// Print, for example, first row values for L_NAME and APP_NAME columns
System.out.println(columns.get("L_NAME").get(0));
System.out.println(columns.get("APP_NAME").get(0));

// Prints:
// leave type 1
// application type 1
于 2014-11-04T23:50:31.337 回答
2

您可以编写一个函数,该函数将根据表名和列名返回列的索引

public int getIndex(ResultSet rs, String table, String column) throws SQLException
{
    for (int i=1; i < rs.getMetaData().getColumnCount(); i++)
    {
        if (rs.getMetaData().getTableName(i).equals(table) && rs.getMetaData().getColumnName(i).equals(column))
        {
            return i;
        }
    }
    return -1;
}

并像这样使用它:

// TODO check if getIndex returns -1
// You can use the appropriate getter off course
System.out.println(rs.getObject(getIndex(rs, "type_application", "name")));
System.out.println(rs.getObject(getIndex(rs, "type_leaves", "name")));
于 2014-10-30T15:34:33.093 回答
1

尝试明确地获取您丢失的列,如下所示:

select *,type_applications.name as type_applications_name
  from leaves, type_applications, type_leaves 
    where leaves.type_leave = type_leaves.id and 
    leaves.type_application = type_applications.id;
  1. 无论如何,不​​鼓励使用交叉连接。
  2. 不鼓励使用 select *。

但是如果你想选择 *无论如何,尝试使用一个表格来包装结果:

select * from (
select leaves.*, type_applications.*, type_leaves.* ,type_applications.name as type_applications_name
  from leaves, type_applications, type_leaves 
    where leaves.type_leave = type_leaves.id and 
    leaves.type_application = type_applications.id;
)

PS:我没有测试过代码,所以可能会出现打字错误,所以只能试着理解一下。

于 2014-11-05T08:45:07.863 回答
0

由于性能,“select *”在最严重的情况下确实是一种不好的做法,我认为即使在 PHP 中也是如此。

此外,即使您使用“select *”,该列也会正确返回,您可以使用 result.getMetaData() 来检查它。

for (int i=1; i<rs.getMetaData().getColumnCount(); i++) {
    System.out.println(rs.getMetaData().getColumnName(i));
}

在 java 世界中,ResultSet 有 getXXXX(String columnName) 方法,因此所有列名在结果中必须是唯一的。但是您也可以使用 getXXXX(int columnIdx) 获取所有列

于 2014-10-29T11:18:04.153 回答
0

在这种情况下,其中一列可能会被重命名。

要解决此问题,您可以从 executeQuery (ResultSet.getMetaData 方法) 返回的结果集中查询列列表。这将返回http://docs.oracle.com/javase/7/docs/api/java/sql/ResultSetMetaData.html的实例

  1. getColumnCount() 将返回结果中的列数
  2. getColumnLabel(int column) 将返回该列的名称

如果两列具有相同的名称“名称”,则这两列的标签可能不同。

于 2014-10-29T11:27:59.587 回答