Выберите префиксные столбцы из нескольких таблиц в Java с MySQL

Мне приходится иметь дело с Java, чтобы создать спокойный API, и я использую его с MySQL для запроса к БД.

Я довольно новичок в Java, и это может быть очень простой вопрос, но я привык к фреймворкам PHP, где этой проблемы не существует, и мне было интересно, как это сделать в Java.

Я запрашиваю 3 таблицы, соединяющие их по внешним ключам, и обнаружил, что если две из этих таблиц имеют одинаковое имя столбца, будет возвращена только одна из них. Тот из последней таблицы, объявленной в from заявление. Кажется, они переопределяют.

Например, если таблицы type_application а также type_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?

5 ответов

Решение

Я тоже новичок в 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

Вы можете написать функцию, которая будет возвращать индекс столбца, основанный как на имени таблицы, так и на имени столбца.

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")));

Попробуйте получить отсутствующий столбец явно, как показано ниже:

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: я не тестировал код, поэтому могут быть ошибки при наборе текста, поэтому постарайтесь понять только идею.

В этом случае один из столбцов может быть переименован.

Чтобы обойти эту проблему, вы можете запросить список столбцов из набора результатов, возвращаемого executeQuery (метод ResultSet.getMetaData) . Это вернет экземпляр http://docs.oracle.com/javase/7/docs/api/java/sql/ResultSetMetaData.html

  1. getColumnCount () вернет количество столбцов в результате
  2. getColumnLabel(int column) вернет имя этого столбца

Если два столбца имеют одинаковое имя "name", метки могут быть разными для этих двух столбцов.

"select *" - это действительно плохая практика в большинстве серьезных случаев из-за производительности, я думаю, что то же самое даже в PHP.

И, кроме того, столбец возвращается правильно, даже если вы используете "select *", вы можете использовать result.getMetaData() для его проверки.

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

В мире Java ResultSet имеет методы getXXXXX(String columnName), поэтому все имена столбцов должны быть уникальными в результате. Но вы также можете использовать getXXXX(int columnIdx) для извлечения всех столбцов

Другие вопросы по тегам