DbTable.java
/*
* Copyright (C) 2000 - 2024 Silverpeas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* As a special exception to the terms and conditions of version 3.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* Open Source Software ("FLOSS") applications as described in Silverpeas's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* "https://www.silverpeas.org/legal/floss_exception.html"
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.silverpeas.components.mydb.model;
import org.silverpeas.components.mydb.model.predicates.AbstractColumnValuePredicate;
import org.silverpeas.components.mydb.model.predicates.ColumnValuePredicate;
import org.silverpeas.components.mydb.service.MyDBRuntimeException;
import org.silverpeas.core.admin.PaginationPage;
import org.silverpeas.core.util.SilverpeasList;
import org.silverpeas.kernel.util.StringUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toMap;
/**
* Table loaded from the database referred by a {@link MyDBConnectionInfo} instance.
* @author mmoquillon
*/
public class DbTable {
private final String name;
private final List<DbColumn> columns = new ArrayList<>();
private JdbcRequester requester = null;
/**
* Loads the default table defined in the specified {@link MyDBConnectionInfo} instance.
* If no default table is defined then nothing is returned. If the default table doesn't
* exist then a {@link org.silverpeas.components.mydb.service.MyDBRuntimeException} is thrown.
* @param dsInfo the {@link MyDBConnectionInfo} instance with information to access the
* database and to get the name of the table load.
* @return optionally a {@link DbTable} instance or nothing if no default table is set in the
* specified {@link MyDBConnectionInfo} instance.
*/
public static Optional<DbTable> defaultTable(final MyDBConnectionInfo dsInfo) {
DbTable table = null;
if (dsInfo.isDefaultTableNameDefined()) {
table = new DbTable(dsInfo.getDefaultTableName(), dsInfo);
}
return Optional.ofNullable(table);
}
/**
* Gets the table with the specified name from the database referenced by the given connection
* information. If no such a table doesn't exist, then nothing is returned.
* @param tableName the name of a table in the database.
* @param dsInfo the {@link MyDBConnectionInfo} instance with information to access the
* database
* @return optionally a {@link DbTable} instance or nothing if no such a table exist in the
* database.
*/
public static Optional<DbTable> table(final String tableName, final MyDBConnectionInfo dsInfo) {
try {
return Optional.of(new DbTable(tableName, dsInfo));
} catch (MyDBRuntimeException e) {
return Optional.empty();
}
}
/**
* Gets a listing of the names of all the business tables in the database. The database is
* identified by the specified {@link MyDBConnectionInfo} instance.
* (The system and technical tables aren't get.)
* @param dsInfo the {@link MyDBConnectionInfo} instance with information to access the
* database.
* @return a list with the name of all the business tables in the database.
*/
public static List<String> list(final MyDBConnectionInfo dsInfo) {
Objects.requireNonNull(dsInfo);
final JdbcRequester requester = new JdbcRequester(dsInfo);
return requester.getTableNames();
}
/**
* Constructs a new instance for a table with the specified name and that is defined in the
* database referenced by the specified {@link MyDBConnectionInfo} object.
* If the table doesn't exist then a
* {@link org.silverpeas.components.mydb.service.MyDBRuntimeException} is thrown.
* @param name the name of the table
* @param ds the information about the database in which is defined this table.
*/
private DbTable(final String name, final MyDBConnectionInfo ds) {
StringUtil.requireDefined(name);
Objects.requireNonNull(ds);
this.name = name;
setJdbcRequester(new JdbcRequester(ds));
}
/**
* Sets a requester to access the database and performs JDBC operations for this table. Once
* a requester set, the columns of the table are automatically fetched.
* @param requester a {@link JdbcRequester} instance.
*/
private void setJdbcRequester(final JdbcRequester requester) {
this.requester = requester;
this.requester.perform((r, c) -> {
this.columns.clear();
r.loadColumns(c, this.name, d -> this.columns.add(new DbColumn(d)));
return null;
});
}
/**
* Gets this table's name.
* @return the name of this table.
*/
public String getName() {
return name;
}
/**
* Gets all the columns that made up this table. If no columns were specified for this table,
* then an empty list is returned.
* @return a list of columns.
*/
public List<DbColumn> getColumns() {
return this.columns;
}
/**
* Gets the column with the specified name.
* @param name the name of the column.
* @return optionally the asked column. If no such column exists then an empty optional is
* returned.
*/
public Optional<DbColumn> getColumn(final String name) {
return this.columns.stream().filter(c -> c.getName().equals(name)).findFirst();
}
/**
* Gets the contents of this table as a list of rows, each of them being a tuple valuing
* all the columns of this table. If the table is empty, then an empty list is returned.
* @param filter a predicate to use for filtering the table content.
* @param orderBy a order by directive already built (without the clause key words).
* @param pagination a pagination in order to avoid bad performances.
* @return a list of table rows. If a filter is set, it is then applied when requesting the
* content of this table. The number of table rows is limited by the
* {@link MyDBConnectionInfo#getDataMaxNumber()} property.
*/
public SilverpeasList<TableRow> getRows(final ColumnValuePredicate filter, final String orderBy,
final PaginationPage pagination) {
if (!(filter instanceof AbstractColumnValuePredicate)) {
throw new IllegalArgumentException(
"DbTable doesn't support predicate other than AbstractColumnValuePredicate objects");
}
return requester.perform((r, c) -> {
final JdbcRequester.DataConverters<TableFieldValue, TableRow> converters =
new JdbcRequester.DataConverters<>(TableFieldValue::new, TableRow::new);
return r.request(c, this.name, (AbstractColumnValuePredicate) filter, orderBy, converters,
pagination);
});
}
/**
* Deletes the specified row.
* @param row the row to delete in this database table.
*/
public long delete(final TableRow row) {
return requester.perform((r, c) -> {
final Map<String, Object> criteria = getCriteriaFrom(row);
return r.delete(c, getName(), criteria);
});
}
/**
* Updates the specified row with the specified other row.
* @param actualRow the row currently in this table.
* @param updatedRow the row that will replace the actual one in this table.
*/
public long update(final TableRow actualRow, final TableRow updatedRow) {
return requester.perform((r, c) -> {
final Map<String, Object> criteria = getCriteriaFrom(actualRow);
final Map<String, Object> values = tableRowToMap(updatedRow);
return r.update(c, getName(), values, criteria);
});
}
/**
* Adds the specified row into this table. The row will be inserted into the database table.
* @param row the row to add into the database table.
*/
public void add(final TableRow row) {
requester.perform((r, c) -> {
final Map<String, Object> values = tableRowToMap(row);
r.insert(c, getName(), values);
return null;
});
}
private Map<String, Object> getCriteriaFrom(final TableRow row) {
final List<DbColumn> pkColumns =
columns.stream().filter(DbColumn::isPrimaryKey).collect(Collectors.toList());
final Map<String, Object> criteria;
if (!pkColumns.isEmpty()) {
criteria = pkColumns.stream()
.collect(toMap(DbColumn::getName,
pk -> row.getFieldValue(pk.getName()).toSQLObject()));
} else {
criteria = tableRowToMap(row);
}
return criteria;
}
private Map<String, Object> tableRowToMap(final TableRow tableRow) {
return tableRow.getFields()
.entrySet()
.stream()
.filter(Predicate.not(e -> e.getKey().equalsIgnoreCase("SP_MAX_ROW_COUNT")))
.collect(HashMap::new, (h, e) -> h.put(e.getKey(), e.getValue().toSQLObject()), HashMap::putAll);
}
}