// 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.db.oom.naming.ColumnNamingStrategy; import jodd.db.oom.naming.TableNamingStrategy; import jodd.util.StringUtil; import jodd.log.Logger; import jodd.log.LoggerFactory; import java.util.HashMap; import java.util.Map; /** * DbOom manager. Contains various global settings, DB-OOM (database - object-oriented) * mapping definitions, naming conventions etc. * <p> * Important: configure settings before entity registration! * <p> * Mapping definitions are used <b>only</b> by a result set mapper (such as {@link jodd.db.oom.mapper.ResultSetMapper} * to lookup for an entity from table name. Table names are read from result-set meta data, for example. * Moreover, it is not needed to use mappings at all: in that case just provide entity types during result set to * objects conversion. * * @see jodd.db.DbManager */ public class DbOomManager { private static final Logger log = LoggerFactory.getLogger(DbOomManager.class); // ---------------------------------------------------------------- singleton private static DbOomManager dbOomManager = new DbOomManager(); /** * Returns DbOom manager instance. */ public static DbOomManager getInstance() { return dbOomManager; } /** * Sets new DbOom manager. */ public static void setInstance(DbOomManager oomManager) { dbOomManager = oomManager; } /** * Resets DbOom Manager to defaults. * It's done by simply creating a new instance * of DbOom manager. */ public static void resetAll() { dbOomManager = new DbOomManager(); } // ---------------------------------------------------------------- naming protected String schemaName; protected TableNamingStrategy tableNames = new TableNamingStrategy(); protected ColumnNamingStrategy columnNames = new ColumnNamingStrategy(); /** * Returns current table name strategy. */ public TableNamingStrategy getTableNames() { return tableNames; } /** * Sets new table name strategy. */ public void setTableNames(TableNamingStrategy tableNames) { this.tableNames = tableNames; } /** * Returns current column name strategy. */ public ColumnNamingStrategy getColumnNames() { return columnNames; } /** * Sets new column name strategy, */ public void setColumnNames(ColumnNamingStrategy columnNames) { this.columnNames = columnNames; } // ---------------------------------------------------------------- registration protected String[] primitiveEntitiesPrefixes = new String[] { "java.lang.", "jodd.mutable.", int.class.getName(), long.class.getName(), float.class.getName(), double.class.getName(), short.class.getName(), boolean.class.getName(), byte.class.getName()}; public String[] getPrimitiveEntitiesPrefixes() { return primitiveEntitiesPrefixes; } /** * Specifies array of class name prefixes that will be mapped directly to column. */ public void setPrimitiveEntitiesPrefixes(String... primitiveEntitiesPrefixes) { this.primitiveEntitiesPrefixes = primitiveEntitiesPrefixes; } protected Map<Class, DbEntityDescriptor> descriptorsMap = new HashMap<>(); protected Map<String, DbEntityDescriptor> entityNamesMap = new HashMap<>(); protected Map<String, DbEntityDescriptor> tableNamesMap = new NamedValuesHashMap<>(); /** * Lookups {@link DbEntityDescriptor} for some type and registers the type if is new. * <p> * Returns <code>null</code> for core classes from <code>java</code> run-time packages! * Some types are <b>not</b> entities, i.e. domain objects. Instead, primitive entities * are simply mapped to one column. */ public <E> DbEntityDescriptor<E> lookupType(Class<E> type) { String typeName = type.getName(); if (StringUtil.startsWithOne(typeName, primitiveEntitiesPrefixes) != -1) { return null; } DbEntityDescriptor<E> ded = descriptorsMap.get(type); if (ded == null) { ded = registerType(type); } return ded; } /** * Returns <code>true</code> if type is registered withing manager. */ public boolean isRegistered(Class type) { return descriptorsMap.containsKey(type); } /** * Lookups for {@link jodd.db.oom.DbEntityDescriptor} that was registered with this manager. * Returns <code>null</code> if name not found. */ public DbEntityDescriptor lookupName(String typeName) { return entityNamesMap.get(typeName); } /** * Lookups for {@link jodd.db.oom.DbEntityDescriptor} that was registered with this manager. * Returns <code>null</code> if table name not found. Lookup is case-insensitive. */ public DbEntityDescriptor lookupTableName(String tableName) { return tableNamesMap.get(tableName); } /** * Registers just type and entity names. Enough for most usages. */ public <E> DbEntityDescriptor<E> registerType(Class<E> type) { DbEntityDescriptor<E> ded = createDbEntityDescriptor(type); DbEntityDescriptor<E> existing = descriptorsMap.put(type, ded); if (log.isDebugEnabled()) { log.debug("Register " + type.getName() + " as " + ded.getTableName()); } if (existing != null) { if (ded.getType() == type) { return ded; } throw new DbOomException("Type already registered: " + existing.getType()); } existing = entityNamesMap.put(ded.getEntityName(), ded); if (existing != null) { throw new DbOomException("Name '" + ded.getEntityName() + "' already mapped to an entity: " + existing.getType()); } return ded; } /** * Registers entity. {@link #registerType(Class) Registers types} and table names. */ public <E> DbEntityDescriptor<E> registerEntity(Class<E> type) { DbEntityDescriptor<E> ded = registerType(type); DbEntityDescriptor existing = tableNamesMap.put(ded.getTableName(), ded); if (existing != null) { if (ded.getType() == type) { return ded; } throw new DbOomException("Entity registration failed! Table '" + ded.getTableName() + "' already mapped to an entity: " + existing.getType()); } return ded; } /** * Registers entity. Existing entity will be removed if exist, so no exception will be thrown. */ public <E> DbEntityDescriptor<E> registerEntity(Class<E> type, boolean force) { if (force) { removeEntity(type); } return registerEntity(type); } /** * Removes entity and returns removed descriptor. */ public <E> DbEntityDescriptor<E> removeEntity(Class<E> type) { DbEntityDescriptor<E> ded = descriptorsMap.remove(type); if (ded == null) { ded = createDbEntityDescriptor(type); } entityNamesMap.remove(ded.getEntityName()); tableNamesMap.remove(ded.getTableName()); return ded; } /** * Creates {@link DbEntityDescriptor}. */ protected <E> DbEntityDescriptor<E> createDbEntityDescriptor(Class<E> type) { return new DbEntityDescriptor<>(type, schemaName, tableNames, columnNames); } // ---------------------------------------------------------------- stats /** * Returns total number of registered entity names. */ public int getTotalNames() { return entityNamesMap.size(); } /** * Returns total number of registered table names. */ public int getTotalTableNames() { return tableNamesMap.size(); } /** * Returns total number of registered types. */ public int getTotalTypes() { return descriptorsMap.size(); } /** * Resets the manager and clears descriptors maps. * The configuration is not changed, just table-related * data is cleared. To reset all, call {@link #resetAll()}. */ public void reset() { descriptorsMap.clear(); entityNamesMap.clear(); tableNamesMap.clear(); } // ---------------------------------------------------------------- table separators protected String columnAliasSeparator = "$"; /** * Returns value for separator for column aliases that divides table reference and column name. */ public String getColumnAliasSeparator() { return columnAliasSeparator; } /** * Specifies separator for column aliases that divides table reference and column name. * Separator should contains of characters that are not used in table names, such as: * '$' or '__'. */ public void setColumnAliasSeparator(String separator) { this.columnAliasSeparator = separator; } // ---------------------------------------------------------------- hint resolver protected JoinHintResolver hintResolver = new JoinHintResolver(); public JoinHintResolver getHintResolver() { return hintResolver; } /** * Specifies hint resolver. */ public void setHintResolver(JoinHintResolver hintResolver) { this.hintResolver = hintResolver; } // ---------------------------------------------------------------- default column alias type protected ColumnAliasType defaultColumnAliasType; public ColumnAliasType getDefaultColumnAliasType() { return defaultColumnAliasType; } /** * Specifies default column alias type. */ public void setDefaultColumnAliasType(ColumnAliasType defaultColumnAliasType) { this.defaultColumnAliasType = defaultColumnAliasType; } // ---------------------------------------------------------------- result set mapper protected boolean cacheEntitiesInResultSet; public boolean isCacheEntitiesInResultSet() { return cacheEntitiesInResultSet; } /** * Defines if entities have to be cached in result set. * When cached, more memory is consumed during the existence of * {@link jodd.db.oom.mapper.ResultSetMapper}. */ public void setCacheEntitiesInResultSet(boolean cacheEntitiesInResultSet) { this.cacheEntitiesInResultSet = cacheEntitiesInResultSet; } // ---------------------------------------------------------------- db list protected boolean entityAwareMode; /** * Returns <code>true</code> if entity-aware mode is enabled. */ public boolean isEntityAwareMode() { return entityAwareMode; } /** * Defines entity-aware mode, when resulting collections does not have duplicates. * It make sense to enable it only if {@link #setCacheEntitiesInResultSet(boolean) cache} is set. * Therefore, enabling smart mode will also enable caching. */ public void setEntityAwareMode(boolean entityAwareMode) { if (entityAwareMode) { this.cacheEntitiesInResultSet = true; } this.entityAwareMode = entityAwareMode; } // ---------------------------------------------------------------- create entity /** * Creates new entity instances. */ public <E> E createEntityInstance(Class<E> type) { try { return type.newInstance(); } catch (Exception ex) { throw new DbOomException(ex); } } }