package org.openlca.core.database;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Table;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.RootEntity;
import org.openlca.core.model.descriptors.BaseDescriptor;
import org.openlca.util.Strings;
public class RootEntityDao<T extends RootEntity, V extends BaseDescriptor> extends BaseDao<T> {
private Class<V> descriptorType;
private String entityTable;
public RootEntityDao(Class<T> entityType, Class<V> descriptorType, IDatabase database) {
super(entityType, database);
this.descriptorType = descriptorType;
}
Class<V> getDescriptorType() {
return descriptorType;
}
public V getDescriptor(long id) {
String sql = getDescriptorQuery() + " where id = ?";
Object[] result = selectFirst(sql, getDescriptorFields(), Collections.singletonList((Object) id));
return createDescriptor(result);
}
public List<V> getDescriptors(Set<Long> ids) {
if (ids == null || ids.isEmpty())
return Collections.emptyList();
if (ids.size() > MAX_LIST_SIZE)
return executeChunked(ids, this::getDescriptors);
String sql = getDescriptorQuery() + " where id in (" + Strings.join(ids, ',') + ")";
List<Object[]> results = selectAll(sql, getDescriptorFields(), Collections.emptyList());
return createDescriptors(results);
}
public List<V> getDescriptorsForRefIds(Set<String> refIds) {
if (refIds == null || refIds.isEmpty())
return Collections.emptyList();
if (refIds.size() > MAX_LIST_SIZE)
return executeChunked(refIds, this::getDescriptorsForRefIds);
Set<String> quotedIds = new HashSet<>();
for (String refId : refIds) {
quotedIds.add('\'' + refId + '\'');
}
String sql = getDescriptorQuery() + " where ref_id in (" + Strings.join(quotedIds, ',') + ")";
List<Object[]> results = selectAll(sql, getDescriptorFields(), Collections.emptyList());
return createDescriptors(results);
}
/**
* Returns all descriptors of the entity type of this DAO from the database.
*/
public List<V> getDescriptors() {
String sql = getDescriptorQuery();
List<Object[]> results = selectAll(sql, getDescriptorFields(), Collections.emptyList());
return createDescriptors(results);
}
protected final String getDescriptorQuery() {
return "select " + Strings.join(getDescriptorFields(), ',') + " from " + getEntityTable();
}
private String getEntityTable() {
if (entityTable == null)
entityTable = entityType.getAnnotation(Table.class).name();
return entityTable;
}
/**
* Returns all fields that should be queried by the descriptor query.
* Subclass may override to provide more information. Use sql column names !
*/
protected String[] getDescriptorFields() {
return new String[] { "id", "ref_id", "name", "description", "version", "last_change" };
}
/**
* Creates a list of descriptors from a list of query results.
*/
protected List<V> createDescriptors(List<Object[]> results) {
if (results == null)
return Collections.emptyList();
List<V> descriptors = new ArrayList<>(results.size());
for (Object[] result : results) {
V descriptor = createDescriptor(result);
if (descriptor != null)
descriptors.add(descriptor);
}
return descriptors;
}
/**
* Creates a descriptor from the given result of a descriptor query. This
* method can be overwritten by subclasses but it must be implemented in a
* way that it matches the respective descriptor query.
*/
protected V createDescriptor(Object[] queryResult) {
if (queryResult == null)
return null;
V descriptor = null;
try {
descriptor = descriptorType.newInstance();
descriptor.setId((Long) queryResult[0]);
descriptor.setRefId((String) queryResult[1]);
descriptor.setName((String) queryResult[2]);
descriptor.setDescription((String) queryResult[3]);
if (queryResult[4] != null)
descriptor.setVersion((long) queryResult[4]);
if (queryResult[5] != null)
descriptor.setLastChange((long) queryResult[5]);
descriptor.setType(ModelType.forModelClass(entityType));
} catch (Exception e) {
DatabaseException.logAndThrow(log, "failed to map query result to descriptor", e);
}
return descriptor;
}
public T getForRefId(String refId) {
if (refId == null)
return null;
String jpql = "select e from " + entityType.getSimpleName() + " e where e.refId = :refId";
try {
return Query.on(getDatabase()).getFirst(entityType, jpql, Collections.singletonMap("refId", refId));
} catch (Exception e) {
DatabaseException.logAndThrow(log, "failed to get instance for refId " + refId, e);
return null;
}
}
public List<T> getForRefIds(Set<String> refIds) {
if (refIds == null || refIds.isEmpty())
return Collections.emptyList();
if (refIds.size() > MAX_LIST_SIZE)
return executeChunked(refIds, this::getForRefIds);
String jpql = "select e from " + entityType.getSimpleName() + " e where e.refId in :refIds";
try {
return Query.on(getDatabase()).getAll(entityType, jpql, Collections.singletonMap("refIds", refIds));
} catch (Exception e) {
DatabaseException.logAndThrow(log, "failed to get instance for refId list", e);
return null;
}
}
/**
* Returns true if an entity with the given reference ID is in the database.
*/
public boolean contains(String refId) {
try (Connection con = getDatabase().createConnection()) {
String query = "select count(*) from " + getEntityTable() + " where ref_id = ?";
PreparedStatement stmt = con.prepareStatement(query);
stmt.setString(1, refId);
ResultSet rs = stmt.executeQuery();
rs.next();
boolean b = rs.getLong(1) > 0;
rs.close();
stmt.close();
return b;
} catch (Exception e) {
DatabaseException.logAndThrow(log, "contains query failed for refId=" + refId, e);
return false;
}
}
public List<T> getForName(String name) {
try {
return Query.on(getDatabase()).getAllForName(entityType, name);
} catch (Exception e) {
DatabaseException.logAndThrow(log, "failed to get instance for name " + name, e);
return null;
}
}
}