All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jodd.db.oom.DbOomQuery Maven / Gradle / Ivy

Go to download

Jodd DB is efficient and thin database facade; DbOom is convenient database object mapper.

There is a newer version: 6.0.0
Show newest version
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.db.oom;

import jodd.bean.BeanUtil;
import jodd.db.DbOom;
import jodd.db.DbQuery;
import jodd.db.DbSession;
import jodd.db.DbUtil;
import jodd.db.oom.mapper.DefaultResultSetMapper;
import jodd.db.oom.mapper.ResultSetMapper;
import jodd.db.oom.sqlgen.ParameterValue;
import jodd.db.type.SqlType;
import jodd.util.CharUtil;
import jodd.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A simple ORM extension for {@link DbQuery}.
 * 

* OOM extension may map results to objects in two ways: *

    *
  • auto mode - when result set is mapped to provided types, and
  • *
  • mapped mode - requires explicit mapping definitions.
  • *
*/ public class DbOomQuery extends DbQuery { private static final Logger log = LoggerFactory.getLogger(DbOomQuery.class); // ---------------------------------------------------------------- default ctors public DbOomQuery(final DbOom dbOom, final Connection conn, final String sqlString) { super(dbOom, conn, sqlString); this.sqlgen = null; init(dbOom); } public static DbOomQuery query(final Connection conn, final String sqlString) { return new DbOomQuery(DbOom.get(), conn, sqlString); } public DbOomQuery(final DbOom dbOom, final DbSession session, final String sqlString) { super(dbOom, session, sqlString); this.sqlgen = null; init(dbOom); } public static DbOomQuery query(final DbSession session, final String sqlString) { return new DbOomQuery(DbOom.get(), session, sqlString); } public DbOomQuery(final DbOom dbOom, final String sqlString) { super(dbOom, sqlString); this.sqlgen = null; init(dbOom); } public static DbOomQuery query(final String sqlString) { return new DbOomQuery(DbOom.get(), sqlString); } // ---------------------------------------------------------------- sqlgen ctors protected final DbSqlGenerator sqlgen; public DbOomQuery(final DbOom dbOom, final Connection conn, final DbSqlGenerator sqlgen) { super(dbOom, conn, sqlgen.generateQuery()); this.sqlgen = sqlgen; init(dbOom); } public static DbOomQuery query(final Connection conn, final DbSqlGenerator sqlgen) { return new DbOomQuery(DbOom.get(), conn, sqlgen); } public DbOomQuery(final DbOom dbOom, final DbSession session, final DbSqlGenerator sqlgen) { super(dbOom, session, sqlgen.generateQuery()); this.sqlgen = sqlgen; init(dbOom); } public static DbOomQuery query(final DbSession session, final DbSqlGenerator sqlgen) { return new DbOomQuery(DbOom.get(), session, sqlgen); } public DbOomQuery(final DbOom dbOom, final DbSqlGenerator sqlgen) { super(dbOom, sqlgen.generateQuery()); this.sqlgen = sqlgen; init(dbOom); } public static DbOomQuery query(final DbSqlGenerator sqlgen) { return new DbOomQuery(DbOom.get(), sqlgen); } protected void init(final DbOom dbOom) { this.sqlString = preprocessSql(sqlString); this.cacheEntities = dbOom.config().isCacheEntitiesInResultSet(); this.entityAwareMode = dbOom.config().isEntityAwareMode(); } // ---------------------------------------------------------------- initialization /** * Prepares the query after initialization. Besides default work, it checks if sql generator * is used, and if so, generator hints and query parameters will be used for this query. * Note regarding hints: since hints can be added manually, generators hints will be ignored * if there exists some manually set hints. */ @Override protected void prepareQuery() { super.prepareQuery(); if (sqlgen == null) { return; } if (hints == null) { final String[] joinHints = sqlgen.getJoinHints(); if (joinHints != null) { withHints(joinHints); } } // insert parameters final Map parameters = sqlgen.getQueryParameters(); if (parameters == null) { return; } for (final Map.Entry entry : parameters.entrySet()) { final String paramName = entry.getKey(); final ParameterValue param = entry.getValue(); final DbEntityColumnDescriptor dec = param.getColumnDescriptor(); if (dec == null) { setObject(paramName, param.getValue()); } else { resolveColumnDbSqlType(connection, dec); setObject(paramName, param.getValue(), dec.getSqlTypeClass(), dec.getDbSqlType()); } } } /** * Resolves column db sql type and populates it in column descriptor if missing. */ protected void resolveColumnDbSqlType(final Connection connection, final DbEntityColumnDescriptor dec) { if (dec.dbSqlType != SqlType.DB_SQLTYPE_UNKNOWN) { return; } ResultSet rs = null; final DbEntityDescriptor ded = dec.getDbEntityDescriptor(); try { final DatabaseMetaData dmd = connection.getMetaData(); rs = dmd.getColumns(null, ded.getSchemaName(), ded.getTableName(), dec.getColumnName()); if (rs.next()) { dec.dbSqlType = rs.getInt("DATA_TYPE"); } else { dec.dbSqlType = SqlType.DB_SQLTYPE_NOT_AVAILABLE; if (log.isWarnEnabled()) { log.warn("Column SQL type not available: " + ded.toString() + '.' + dec.getColumnName()); } } } catch (final SQLException sex) { dec.dbSqlType = SqlType.DB_SQLTYPE_NOT_AVAILABLE; if (log.isWarnEnabled()) { log.warn("Column SQL type not resolved: " + ded.toString() + '.' + dec.getColumnName(), sex); } } finally { DbUtil.close(rs); } } /** * Pre-process SQL before using it. If string starts with a non-ascii char * or it has no spaces, it will be loaded from the query map. */ protected String preprocessSql(String sqlString) { // detects callable statement if (sqlString.charAt(0) == '{') { return sqlString; } // quickly detect if SQL string is a key if (!CharUtil.isAlpha(sqlString.charAt(0))) { sqlString = sqlString.substring(1); } else if (sqlString.indexOf(' ') != -1) { return sqlString; } final String sqlFromMap = dbOom.queryMap().getQuery(sqlString); if (sqlFromMap != null) { sqlString = sqlFromMap.trim(); } return sqlString; } // ---------------------------------------------------------------- join hints protected JoinHintResolver hintResolver; protected String[] hints; /** * Specifies hints for the query. Provided string is * split on ',' separator. */ public DbOomQuery withHints(final String hint) { this.hints = StringUtil.splitc(hint, ','); return this; } /** * Specifies multiple hints for the query. */ public DbOomQuery withHints(final String... hints) { this.hints = hints; return this; } /** * Prepares a row (array of rows mapped object) using hints. * Returns either single object or objects array. */ protected Object resolveRowResults(Object[] row) { if (hintResolver == null) { hintResolver = new JoinHintResolver(); } row = hintResolver.join(row, hints); return row.length == 1 ? row[0] : row; } // ---------------------------------------------------------------- result set protected boolean cacheEntities; /** * Defines if entities should be cached in {@link ResultSetMapper}. * Overrides default value in {@link DbEntityManager}. */ public DbOomQuery cacheEntities(final boolean cacheEntities) { this.cacheEntities = cacheEntities; return this; } /** * Executes the query and returns {@link #createResultSetMapper(java.sql.ResultSet) builded ResultSet mapper}. */ protected ResultSetMapper executeAndBuildResultSetMapper() { final ResultSet resultSet = execute(); return createResultSetMapper(resultSet); } /** * Factory for result sets mapper. */ protected ResultSetMapper createResultSetMapper(final ResultSet resultSet) { final Map columnAliases = sqlgen != null ? sqlgen.getColumnData() : null; return new DefaultResultSetMapper(dbOom, resultSet, columnAliases, cacheEntities, this); } // ---------------------------------------------------------------- db list protected boolean entityAwareMode; /** * Defines entity-aware mode for entities tracking in result collection. */ public DbOomQuery entityAwareMode(final boolean entityAware) { if (entityAware) { this.cacheEntities = true; } this.entityAwareMode = entityAware; return this; } // ---------------------------------------------------------------- iterator public Iterator iterate(final Class... types) { return iterate(types, autoClose); } public Iterator iterate() { return iterate(null, autoClose); } protected Iterator iterate(final Class[] types, final boolean close) { return new DbListIterator<>(this, types, close); } // ---------------------------------------------------------------- list public List list(final Class... types) { return list(types, -1, autoClose); } public List list() { return list(null, -1, autoClose); } public List list(final int max, final Class... types) { return list(types, max, autoClose); } public List list(final int max) { return list(null, max, autoClose); } /** * Iterates result set, maps rows to classes and populates resulting array list. * @param types mapping types * @param max max number of rows to collect, -1 for all * @param close true if query is closed at the end, otherwise false. * @return list of mapped entities or array of entities */ @SuppressWarnings({"unchecked"}) protected List list(Class[] types, final int max, final boolean close) { final List result = new ArrayList<>(initialCollectionSize(max)); final ResultSetMapper rsm = executeAndBuildResultSetMapper(); if (types == null) { types = rsm.resolveTables(); } Object previousElement = null; while (rsm.next()) { final Object[] objects = rsm.parseObjects(types); final Object row = resolveRowResults(objects); final int size = result.size(); final T newElement = (T) row; if (entityAwareMode && size > 0) { if (previousElement != null && newElement != null) { final boolean equals; if (newElement.getClass().isArray()) { equals = Arrays.equals((Object[]) previousElement, (Object[]) newElement); } else { equals = previousElement.equals(newElement); } if (equals) { continue; } } } if (size == max) { break; } result.add(newElement); previousElement = newElement; } close(rsm, close); return result; } /** * Returns initial collections size when max * value is provided. */ private static int initialCollectionSize(final int max) { return max > 0 ? max : 10; } // ---------------------------------------------------------------- set public Set listSet(final Class... types) { return listSet(types, -1, autoClose); } public Set listSet() { return listSet(null, -1, autoClose); } public Set listSet(final int max, final Class... types) { return listSet(types, max, autoClose); } public Set listSet(final int max) { return listSet(null, max, autoClose); } @SuppressWarnings({"unchecked"}) protected Set listSet(Class[] types, final int max, final boolean close) { final Set result = new LinkedHashSet<>(initialCollectionSize(max)); final ResultSetMapper rsm = executeAndBuildResultSetMapper(); if (types == null) { types = rsm.resolveTables(); } Object previousElement = null; while (rsm.next()) { final Object[] objects = rsm.parseObjects(types); final Object row = resolveRowResults(objects); final int size = result.size(); final T newElement = (T) row; if (entityAwareMode && size > 0) { if (previousElement != null && newElement != null) { final boolean equals; if (newElement.getClass().isArray()) { equals = Arrays.equals((Object[]) previousElement, (Object[]) newElement); } else { equals = previousElement.equals(newElement); } if (equals) { continue; } } } if (size == max) { break; } result.add(newElement); previousElement = newElement; } close(rsm, close); return result; } // ---------------------------------------------------------------- find public T find(final Class... types) { return find(types, autoClose, null); } public T find() { return find(null, autoClose, null); } protected T find(final Class[] types, final boolean close, ResultSet resultSet) { if (resultSet == null) { resultSet = execute(); } final ResultSetMapper rsm = createResultSetMapper(resultSet); final Iterator iterator = new DbListIterator<>(this, types, rsm, false); T result = null; if (iterator.hasNext()) { result = iterator.next(); } close(rsm, close); return result; } // ---------------------------------------------------------------- generated columns /** * Finds generated key column of given type. */ public T findGeneratedKey(final Class type) { return find(new Class[] {type}, false, getGeneratedColumns()); } /** * Finds generated columns. */ public Object findGeneratedColumns(final Class... types) { return find(types, false, getGeneratedColumns()); } /** * Populates entity with generated column values from executed query. */ public void populateGeneratedKeys(final Object entity) { final String[] generatedColumns = getGeneratedColumnNames(); if (generatedColumns == null) { return; } final DbEntityDescriptor ded = dbOom.entityManager().lookupType(entity.getClass()); // prepare key types final Class[] keyTypes = new Class[generatedColumns.length]; final String[] properties = new String[generatedColumns.length]; for (int i = 0; i < generatedColumns.length; i++) { final String column = generatedColumns[i]; final DbEntityColumnDescriptor decd = ded.findByColumnName(column); if (decd != null) { keyTypes[i] = decd.getPropertyType(); properties[i] = decd.getPropertyName(); } } final Object keyValues = findGeneratedColumns(keyTypes); if (!keyValues.getClass().isArray()) { BeanUtil.declared.setProperty(entity, properties[0], keyValues); } else { for (int i = 0; i < properties.length; i++) { BeanUtil.declared.setProperty(entity, properties[i], ((Object[]) keyValues)[i]); } } } // ---------------------------------------------------------------- util /** * {@inheritDoc} */ @Override public DbOomQuery autoClose() { super.autoClose(); return this; } /** * Closes results set or whole query. */ protected void close(final ResultSetMapper rsm, final boolean closeQuery) { if (closeQuery) { close(); } else { closeResultSet(rsm.getResultSet()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy