Выберите префиксные столбцы из нескольких таблиц в 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;
- В любом случае, использование перекрестных соединений не рекомендуется.
- Использование 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
- getColumnCount () вернет количество столбцов в результате
- 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) для извлечения всех столбцов