Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.hibernate.cfg.JDBCBinder Maven / Gradle / Ivy
/*
* Created on 2004-11-23
*
*/
package org.hibernate.cfg;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.hibernate.DuplicateMappingException;
import org.hibernate.FetchMode;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.internal.InFlightMetadataCollectorImpl;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.binder.BinderUtils;
import org.hibernate.cfg.binder.PrimaryKeyInfo;
import org.hibernate.cfg.binder.PropertyBinder;
import org.hibernate.cfg.reveng.AssociationInfo;
import org.hibernate.cfg.reveng.DatabaseCollector;
import org.hibernate.cfg.reveng.JDBCReader;
import org.hibernate.cfg.reveng.JDBCToHibernateTypeHelper;
import org.hibernate.cfg.reveng.MappingsDatabaseCollector;
import org.hibernate.cfg.reveng.RevEngUtils;
import org.hibernate.cfg.reveng.ReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.TableIdentifier;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.DependantValue;
import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.MetaAttribute;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.util.TableNameQualifier;
import org.hibernate.type.ForeignKeyDirection;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
/**
* @author max
*
*/
public class JDBCBinder {
private Properties properties;
private static final Logger log = Logger.getLogger(JDBCBinder.class);
private final MetadataBuildingContext mdbc;
private final InFlightMetadataCollector metadataCollector;
private Metadata metadata;
private ReverseEngineeringStrategy revengStrategy;
private final boolean preferBasicCompositeIds;
private final ServiceRegistry serviceRegistry;
private final String defaultCatalog;
private final String defaultSchema;
/**
* @param mappings
* @param configuration
*/
public JDBCBinder(ServiceRegistry serviceRegistry, Properties properties, MetadataBuildingContext mdbc, ReverseEngineeringStrategy revengStrategy, boolean preferBasicCompositeIds) {
this.serviceRegistry = serviceRegistry;
this.mdbc = mdbc;
this.properties = properties;
this.revengStrategy = revengStrategy;
this.preferBasicCompositeIds = preferBasicCompositeIds;
this.defaultCatalog = properties.getProperty(AvailableSettings.DEFAULT_CATALOG);
this.defaultSchema = properties.getProperty(AvailableSettings.DEFAULT_SCHEMA);
metadataCollector = mdbc.getMetadataCollector();
this.metadata = ((InFlightMetadataCollectorImpl)metadataCollector).buildMetadataInstance(mdbc);
}
/**
*
*/
public void readFromDatabase(String catalog, String schema, Mapping mapping) {
try {
DatabaseCollector collector = readDatabaseSchema(catalog, schema);
createPersistentClasses(collector, mapping); //move this to a different step!
((InFlightMetadataCollectorImpl)metadataCollector).processSecondPasses(mdbc);
}
catch (SQLException e) {
JdbcServices jdbcServices = serviceRegistry.getService(JdbcServices.class);
throw jdbcServices.getSqlExceptionHelper().convert(e, "Reading from database", null);
}
}
/**
* Read JDBC Metadata from the database. Does not create any classes or other ORM releated structures.
*
* @param catalog
* @param schema
* @return
* @throws SQLException
*/
public DatabaseCollector readDatabaseSchema(String catalog, String schema) throws SQLException {
// use default from settings if nothing else specified.
catalog = catalog!=null ? catalog : properties.getProperty(AvailableSettings.DEFAULT_CATALOG);
schema = schema!=null ? schema : properties.getProperty(AvailableSettings.DEFAULT_SCHEMA);
JDBCReader reader = JDBCReaderFactory.newJDBCReader(properties,revengStrategy,serviceRegistry);
DatabaseCollector dbs = new MappingsDatabaseCollector(metadataCollector, reader.getMetaDataDialect());
reader.readDatabaseSchema(dbs, catalog, schema);
return dbs;
}
/**
* @param manyToOneCandidates
* @param mappings2
*/
private void createPersistentClasses(DatabaseCollector collector, Mapping mapping) {
Map> manyToOneCandidates = collector.getOneToManyCandidates();
for (Iterator iter = metadataCollector.collectTableMappings().iterator(); iter.hasNext();) {
Table table = iter.next();
if (table.getCatalog() != null && table.getCatalog().equals(defaultCatalog)) {
table.setCatalog(null);
}
if (table.getSchema() != null && table.getSchema().equals(defaultSchema)) {
table.setSchema(null);
}
if(table.getColumnSpan()==0) {
log.warn("Cannot create persistent class for " + table + " as no columns were found.");
continue;
}
// TODO: this naively just create an entity per table
// should have an opt-out option to mark some as helper tables, subclasses etc.
/*if(table.getPrimaryKey()==null || table.getPrimaryKey().getColumnSpan()==0) {
log.warn("Cannot create persistent class for " + table + " as no primary key was found.");
continue;
// TODO: just create one big embedded composite id instead.
}*/
if(revengStrategy.isManyToManyTable(table)) {
log.debug( "Ignoring " + table + " as class since rev.eng. says it is a many-to-many" );
continue;
}
RootClass rc = new RootClass(mdbc);
TableIdentifier tableIdentifier = TableIdentifier.create(table);
String className = revengStrategy.tableToClassName( tableIdentifier );
log.debug("Building entity " + className + " based on " + tableIdentifier);
rc.setEntityName( className );
rc.setJpaEntityName( StringHelper.unqualify( className ) );
rc.setClassName( className );
rc.setProxyInterfaceName( rc.getEntityName() ); // TODO: configurable ?
rc.setLazy(true);
rc.setMetaAttributes(
BinderUtils.safeMap(
RevEngUtils.getTableToMetaAttributesInRevengStrategy(
revengStrategy,
table,
defaultCatalog,
defaultSchema)));
rc.setDiscriminatorValue( rc.getEntityName() );
rc.setTable(table);
try {
metadataCollector.addEntityBinding(rc);
} catch(DuplicateMappingException dme) {
// TODO: detect this and generate a "permutation" of it ?
PersistentClass class1 = metadataCollector.getEntityBinding(dme.getName());
Table table2 = class1.getTable();
throw new JDBCBinderException("Duplicate class name '" + rc.getEntityName() + "' generated for '" + table + "'. Same name where generated for '" + table2 + "'");
}
metadataCollector.addImport( rc.getEntityName(), rc.getEntityName() );
Set processed = new HashSet();
PrimaryKeyInfo pki = bindPrimaryKeyToProperties(table, rc, processed, mapping, collector);
bindColumnsToVersioning(table, rc, processed, mapping);
bindOutgoingForeignKeys(table, rc, processed);
bindColumnsToProperties(table, rc, processed, mapping);
List incomingForeignKeys = manyToOneCandidates.get( rc.getEntityName() );
bindIncomingForeignKeys(rc, processed, incomingForeignKeys, mapping);
updatePrimaryKey(rc, pki);
}
}
private void updatePrimaryKey(RootClass rc, PrimaryKeyInfo pki) {
SimpleValue idValue = (SimpleValue) rc.getIdentifierProperty().getValue();
Properties defaultStrategyProperties = new Properties();
Property constrainedOneToOne = getConstrainedOneToOne(rc);
if(constrainedOneToOne!=null) {
if(pki.suggestedStrategy==null) {
idValue.setIdentifierGeneratorStrategy("foreign");
}
if(pki.suggestedProperties==null) {
defaultStrategyProperties.setProperty("property", constrainedOneToOne.getName());
idValue.setIdentifierGeneratorProperties(defaultStrategyProperties);
}
}
}
private Property getConstrainedOneToOne(RootClass rc) {
Iterator> propertyClosureIterator = rc.getPropertyClosureIterator();
while (propertyClosureIterator.hasNext()) {
Property property = (Property) propertyClosureIterator.next();
if(property.getValue() instanceof OneToOne) {
OneToOne oto = (OneToOne) property.getValue();
if(oto.isConstrained()) {
return property;
}
}
}
return null;
}
// bind collections.
@SuppressWarnings("unchecked")
private void bindIncomingForeignKeys(PersistentClass rc, Set processed, List foreignKeys, Mapping mapping) {
if(foreignKeys!=null) {
for (Iterator iter = foreignKeys.iterator(); iter.hasNext();) {
ForeignKey foreignKey = iter.next();
if(revengStrategy.excludeForeignKeyAsCollection(
foreignKey.getName(),
TableIdentifier.create(foreignKey.getTable() ),
foreignKey.getColumns(),
TableIdentifier.create(foreignKey.getReferencedTable() ),
foreignKey.getReferencedColumns())) {
log.debug("Rev.eng excluded one-to-many or one-to-one for foreignkey " + foreignKey.getName());
} else if (revengStrategy.isOneToOne(foreignKey)){
Property property = bindOneToOne(rc, foreignKey.getTable(), foreignKey, processed, false, true);
rc.addProperty(property);
} else {
Property property = bindOneToMany(rc, foreignKey, processed, mapping);
rc.addProperty(property);
}
}
}
}
private Property bindOneToOne(PersistentClass rc, Table targetTable,
ForeignKey fk, Set processedColumns, boolean constrained, boolean inverseProperty) {
OneToOne value = new OneToOne((MetadataImplementor)metadata, targetTable, rc);
value.setReferencedEntityName(revengStrategy
.tableToClassName(TableIdentifier.create(targetTable)));
boolean isUnique = isUniqueReference(fk);
String propertyName = null;
if(inverseProperty) {
propertyName = revengStrategy.foreignKeyToInverseEntityName(
fk.getName(),
TableIdentifier.create(fk.getReferencedTable()),
fk.getReferencedColumns(),
TableIdentifier.create(targetTable),
fk.getColumns(),
isUnique);
} else {
propertyName = revengStrategy.foreignKeyToEntityName(
fk.getName(),
TableIdentifier.create(fk.getReferencedTable()),
fk.getReferencedColumns(),
TableIdentifier.create(targetTable),
fk.getColumns(),
isUnique);
}
Iterator columns = fk.getColumnIterator();
while (columns.hasNext()) {
Column fkcolumn = (Column) columns.next();
checkColumn(fkcolumn);
value.addColumn(fkcolumn);
processedColumns.add(fkcolumn);
}
value.setFetchMode(FetchMode.SELECT);
value.setConstrained(constrained);
value.setForeignKeyType( constrained ?
ForeignKeyDirection.FROM_PARENT :
ForeignKeyDirection.TO_PARENT );
return makeEntityProperty(propertyName, true, targetTable, fk, value, inverseProperty);
//return makeProperty(TableIdentifier.create(targetTable), propertyName, value,
// true, true, value.getFetchMode() != FetchMode.JOIN, null, null);
}
/**
* @param mutable
* @param table
* @param fk
* @param columnsToBind
* @param processedColumns
* @param rc
* @param propName
*/
private Property bindManyToOne(String propertyName, boolean mutable, Table table, ForeignKey fk, Set processedColumns) {
ManyToOne value = new ManyToOne((MetadataImplementor)metadata, table);
value.setReferencedEntityName( fk.getReferencedEntityName() );
Iterator columns = fk.getColumnIterator();
while ( columns.hasNext() ) {
Column fkcolumn = (Column) columns.next();
checkColumn(fkcolumn);
value.addColumn(fkcolumn);
processedColumns.add(fkcolumn);
}
value.setFetchMode(FetchMode.SELECT);
return makeEntityProperty(propertyName, mutable, table, fk, value, false);
}
private Property makeCollectionProperty(String propertyName, boolean mutable,
Table table, ForeignKey fk, Collection value, boolean inverseProperty) {
AssociationInfo fkei = inverseProperty?revengStrategy.foreignKeyToInverseAssociationInfo(fk):revengStrategy.foreignKeyToAssociationInfo(fk);
String fetchMode = null;
String cascade = null;
boolean update = mutable;
boolean insert = mutable;
if(fkei != null){
cascade = fkei.getCascade();
if(cascade==null) cascade = "all"; //To ensure collections cascade to be compatible with Seam-gen and previous behavior.
if(fkei.getUpdate()!=null) {
update = fkei.getUpdate().booleanValue();
}
if(fkei.getInsert()!=null) {
insert = fkei.getInsert().booleanValue();
}
fetchMode = fkei.getFetch();
}
if(FetchMode.JOIN.toString().equalsIgnoreCase(fetchMode)) {
value.setFetchMode(FetchMode.JOIN);
}
else if(FetchMode.SELECT.toString().equalsIgnoreCase(fetchMode)) {
value.setFetchMode(FetchMode.SELECT);
}
else {
value.setFetchMode(FetchMode.SELECT);
}
return PropertyBinder.makeProperty(
table,
defaultCatalog,
defaultSchema,
propertyName,
value,
insert,
update,
value.getFetchMode()!=FetchMode.JOIN,
cascade,
null,
revengStrategy);
}
private Property makeEntityProperty(String propertyName, boolean mutable,
Table table, ForeignKey fk, ToOne value, boolean inverseProperty) {
AssociationInfo fkei = inverseProperty?revengStrategy.foreignKeyToInverseAssociationInfo(fk):revengStrategy.foreignKeyToAssociationInfo(fk);
String fetchMode = null;
String cascade = null;
boolean update = mutable;
boolean insert = mutable;
if(fkei != null){
cascade = fkei.getCascade();
if(fkei.getUpdate()!=null) {
update = fkei.getUpdate().booleanValue();
}
if(fkei.getInsert()!=null) {
insert = fkei.getInsert().booleanValue();
}
fetchMode = fkei.getFetch();
}
if(FetchMode.JOIN.toString().equalsIgnoreCase(fetchMode)) {
value.setFetchMode(FetchMode.JOIN);
}
else if(FetchMode.SELECT.toString().equalsIgnoreCase(fetchMode)) {
value.setFetchMode(FetchMode.SELECT);
}
else {
value.setFetchMode(FetchMode.SELECT);
}
return PropertyBinder.makeProperty(
table,
defaultCatalog,
defaultSchema,
propertyName,
value,
insert,
update,
value.getFetchMode()!=FetchMode.JOIN,
cascade,
null,
revengStrategy);
}
/**
* @param rc
* @param processed
* @param table
* @param object
*/
private Property bindOneToMany(PersistentClass rc, ForeignKey foreignKey, Set processed, Mapping mapping) {
Table collectionTable = foreignKey.getTable();
Collection collection = new org.hibernate.mapping.Set((MetadataImplementor)metadata, rc); // MASTER TODO: allow overriding collection type
collection.setCollectionTable(collectionTable); // CHILD+
boolean manyToMany = revengStrategy.isManyToManyTable( collectionTable );
if(manyToMany) {
//log.debug("Rev.eng said here is a many-to-many");
// TODO: handle "the other side should influence the name"
}
if(manyToMany) {
ManyToOne element = new ManyToOne((MetadataImplementor)metadata, collection.getCollectionTable() );
//TODO: find the other foreignkey and choose the other side.
Iterator> foreignKeyIterator = foreignKey.getTable().getForeignKeyIterator();
List keys = new ArrayList();
while ( foreignKeyIterator.hasNext() ) {
ForeignKey next = (ForeignKey)foreignKeyIterator.next();
if(next!=foreignKey) {
keys.add(next);
}
}
if(keys.size()>1) {
throw new JDBCBinderException("more than one other foreign key to choose from!"); // todo: handle better ?
}
ForeignKey fk = (ForeignKey) keys.get( 0 );
String tableToClassName = bindCollection( rc, foreignKey, fk, collection );
element.setReferencedEntityName( tableToClassName );
Iterator columnIterator = fk.getColumns().iterator();
while (columnIterator.hasNext()) {
Column fkcolumn = (Column) columnIterator.next();
if(fkcolumn.getSqlTypeCode() != null) { // TODO: user defined foreign ref columns does not have a type set.
guessAndAlignType(fk.getTable(), fkcolumn, mapping, false); // needed to ensure foreign key columns has same type as the "property" column.
}
element.addColumn(fkcolumn);
}
collection.setElement( element );
} else {
String tableToClassName = bindCollection( rc, foreignKey, null, collection );
OneToMany oneToMany = new OneToMany((MetadataImplementor)metadata, collection.getOwner() );
oneToMany.setReferencedEntityName( tableToClassName ); // Child
metadataCollector.addSecondPass( new JDBCCollectionSecondPass(mdbc, collection) );
collection.setElement(oneToMany);
}
// bind keyvalue
KeyValue referencedKeyValue;
String propRef = collection.getReferencedPropertyName();
if (propRef==null) {
referencedKeyValue = collection.getOwner().getIdentifier();
}
else {
referencedKeyValue = (KeyValue) collection.getOwner()
.getProperty(propRef)
.getValue();
}
SimpleValue keyValue = new DependantValue((MetadataImplementor)metadata, collectionTable, referencedKeyValue );
//keyValue.setForeignKeyName("none"); // Avoid creating the foreignkey
//key.setCascadeDeleteEnabled( "cascade".equals( subnode.attributeValue("on-delete") ) );
Iterator columnIterator = foreignKey.getColumnIterator();
while ( columnIterator.hasNext() ) {
Column fkcolumn = columnIterator.next();
if(fkcolumn.getSqlTypeCode()!=null) { // TODO: user defined foreign ref columns does not have a type set.
guessAndAlignType(collectionTable, fkcolumn, mapping, false); // needed to ensure foreign key columns has same type as the "property" column.
}
keyValue.addColumn( fkcolumn );
}
collection.setKey(keyValue);
metadataCollector.addCollectionBinding(collection);
return makeCollectionProperty(StringHelper.unqualify( collection.getRole() ), true, rc.getTable(), foreignKey, collection, true);
//return makeProperty(TableIdentifier.create( rc.getTable() ), StringHelper.unqualify( collection.getRole() ), collection, true, true, true, "none", null); // TODO: cascade isn't all by default
}
private String bindCollection(PersistentClass rc, ForeignKey fromForeignKey, ForeignKey toForeignKey, Collection collection) {
ForeignKey targetKey = fromForeignKey;
String collectionRole = null;
boolean collectionLazy = false;
boolean collectionInverse = false;
TableIdentifier foreignKeyTable = null;
String tableToClassName;
if(toForeignKey!=null) {
targetKey = toForeignKey;
}
boolean uniqueReference = isUniqueReference(targetKey); // TODO: need to look one step further for many-to-many!
foreignKeyTable = TableIdentifier.create( targetKey.getTable() );
TableIdentifier foreignKeyReferencedTable = TableIdentifier.create( targetKey.getReferencedTable() );
if(toForeignKey==null) {
collectionRole = revengStrategy.foreignKeyToCollectionName(
fromForeignKey.getName(),
foreignKeyTable,
fromForeignKey.getColumns(),
foreignKeyReferencedTable,
fromForeignKey.getReferencedColumns(),
uniqueReference
);
tableToClassName = revengStrategy.tableToClassName( foreignKeyTable );
} else {
collectionRole = revengStrategy.foreignKeyToManyToManyName(
fromForeignKey, TableIdentifier.create( fromForeignKey.getTable()), toForeignKey, uniqueReference );
tableToClassName = revengStrategy.tableToClassName( foreignKeyReferencedTable );
}
collectionInverse = revengStrategy.isForeignKeyCollectionInverse(
targetKey.getName(),
foreignKeyTable,
targetKey.getColumns(),
foreignKeyReferencedTable,
targetKey.getReferencedColumns());
collectionLazy = revengStrategy.isForeignKeyCollectionLazy(
targetKey.getName(),
foreignKeyTable,
targetKey.getColumns(),
foreignKeyReferencedTable,
targetKey.getReferencedColumns());
collectionRole = BinderUtils.makeUnique(rc,collectionRole);
String fullRolePath = StringHelper.qualify(rc.getEntityName(), collectionRole);
if (metadata.getCollectionBinding(fullRolePath)!=null) {
log.debug(fullRolePath + " found twice!");
}
collection.setRole(fullRolePath); // Master.setOfChildren+
collection.setInverse(collectionInverse); // TODO: allow overriding this
collection.setLazy(collectionLazy);
collection.setFetchMode(FetchMode.SELECT);
return tableToClassName;
}
/** return true if this foreignkey is the only reference from this table to the same foreign table */
private boolean isUniqueReference(ForeignKey foreignKey) {
Iterator> foreignKeyIterator = foreignKey.getTable().getForeignKeyIterator();
while ( foreignKeyIterator.hasNext() ) {
ForeignKey element = (ForeignKey) foreignKeyIterator.next();
if(element!=foreignKey && element.getReferencedTable().equals(foreignKey.getReferencedTable())) {
return false;
}
}
return true;
}
private PrimaryKeyInfo bindPrimaryKeyToProperties(Table table, RootClass rc, Set processed, Mapping mapping, DatabaseCollector collector) {
SimpleValue id = null;
String idPropertyname = null;
PrimaryKeyInfo pki = new PrimaryKeyInfo();
List keyColumns = null;
if (table.getPrimaryKey()!=null) {
keyColumns = table.getPrimaryKey().getColumns();
}
else {
log.debug("No primary key found for " + table + ", using all properties as the identifier.");
keyColumns = new ArrayList();
Iterator> iter = table.getColumnIterator();
while (iter.hasNext() ) {
Column col = (Column) iter.next();
keyColumns.add(col);
}
}
final TableIdentifier tableIdentifier = TableIdentifier.create(table);
String tableIdentifierStrategyName = "assigned";
boolean naturalId;
if (keyColumns.size()>1) {
log.debug("id strategy for " + rc.getEntityName() + " since it has a multiple column primary key");
naturalId = true;
id = handleCompositeKey(rc, processed, keyColumns, mapping);
idPropertyname = revengStrategy.tableToIdentifierPropertyName(tableIdentifier);
if(idPropertyname==null) {
idPropertyname = "id";
}
}
else {
pki.suggestedStrategy = RevEngUtils.getTableIdentifierStrategyNameInRevengStrategy(revengStrategy, table, defaultCatalog, defaultSchema);
String suggestedStrategy = pki.suggestedStrategy;
if(suggestedStrategy==null) {
suggestedStrategy = collector.getSuggestedIdentifierStrategy( tableIdentifier.getCatalog(), tableIdentifier.getSchema(), tableIdentifier.getName() );
if(suggestedStrategy==null) {
suggestedStrategy = "assigned";
}
tableIdentifierStrategyName = suggestedStrategy;
} else {
tableIdentifierStrategyName = suggestedStrategy;
}
naturalId = "assigned".equals( tableIdentifierStrategyName );
Column pkc = (Column) keyColumns.get(0);
checkColumn(pkc);
id = bindColumnToSimpleValue(table, pkc, mapping, !naturalId);
idPropertyname = revengStrategy.tableToIdentifierPropertyName(tableIdentifier);
if(idPropertyname==null) {
idPropertyname = revengStrategy.columnToPropertyName(tableIdentifier, pkc.getName() );
}
processed.add(pkc);
}
id.setIdentifierGeneratorStrategy(tableIdentifierStrategyName);
pki.suggestedProperties = revengStrategy.getTableIdentifierProperties(tableIdentifier);
id.setIdentifierGeneratorProperties(pki.suggestedProperties);
if(naturalId) {
id.setNullValue("undefined");
}
Property property = PropertyBinder.makeProperty(
table,
defaultCatalog,
defaultSchema,
BinderUtils.makeUnique(rc,idPropertyname),
id,
true,
true,
false,
null,
null,
revengStrategy);
rc.setIdentifierProperty(property);
rc.setIdentifier(id);
return pki;
}
/**
* bind many-to-ones
* @param table
* @param rc
* @param primaryKey
*/
private void bindOutgoingForeignKeys(Table table, RootClass rc, Set processedColumns) {
// Iterate the outgoing foreign keys and create many-to-one's
for(Iterator> iterator = table.getForeignKeyIterator(); iterator.hasNext();) {
ForeignKey foreignKey = (ForeignKey) iterator.next();
boolean mutable = true;
if ( contains( foreignKey.getColumnIterator(), processedColumns ) ) {
if ( !preferBasicCompositeIds ) continue; //it's in the pk, so skip this one
mutable = false;
}
if(revengStrategy.excludeForeignKeyAsManytoOne(foreignKey.getName(),
TableIdentifier.create(foreignKey.getTable() ),
foreignKey.getColumns(),
TableIdentifier.create(foreignKey.getReferencedTable() ),
foreignKey.getReferencedColumns())) {
// TODO: if many-to-one is excluded should the column be marked as processed so it won't show up at all ?
log.debug("Rev.eng excluded *-to-one for foreignkey " + foreignKey.getName());
} else if (revengStrategy.isOneToOne(foreignKey)){
Property property = bindOneToOne(rc, foreignKey.getReferencedTable(), foreignKey, processedColumns, true, false);
rc.addProperty(property);
} else {
boolean isUnique = isUniqueReference(foreignKey);
String propertyName = revengStrategy.foreignKeyToEntityName(
foreignKey.getName(),
TableIdentifier.create(foreignKey.getTable() ),
foreignKey.getColumns(),
TableIdentifier.create(foreignKey.getReferencedTable() ),
foreignKey.getReferencedColumns(),
isUnique
);
Property property = bindManyToOne(
BinderUtils.makeUnique(rc, propertyName),
mutable,
table,
foreignKey,
processedColumns
);
rc.addProperty(property);
}
}
}
/**
* @param table
* @param rc
* @param primaryKey
*/
private void bindColumnsToProperties(Table table, RootClass rc, Set processedColumns, Mapping mapping) {
for (Iterator> iterator = table.getColumnIterator(); iterator.hasNext();) {
Column column = (Column) iterator.next();
if ( !processedColumns.contains(column) ) {
checkColumn(column);
String propertyName =
RevEngUtils.getColumnToPropertyNameInRevengStrategy(
revengStrategy,
table,
defaultCatalog,
defaultSchema,
column.getName());
Property property = bindBasicProperty(
BinderUtils.makeUnique(rc,propertyName),
table,
column,
processedColumns,
mapping
);
rc.addProperty(property);
}
}
}
private void bindColumnsToVersioning(Table table, RootClass rc, Set processed, Mapping mapping) {
TableIdentifier identifier = TableIdentifier.create(table);
String optimisticLockColumnName = revengStrategy.getOptimisticLockColumnName(identifier);
if(optimisticLockColumnName!=null) {
Column column = table.getColumn(new Column(optimisticLockColumnName));
if(column==null) {
log.warn("Column " + column + " wanted for / not found in " + identifier);
} else {
bindVersionProperty(table, identifier, column, rc, processed, mapping);
}
} else {
log.debug("Scanning " + identifier + " for / columns.");
Iterator> columnIterator = table.getColumnIterator();
while(columnIterator.hasNext()) {
Column column = (Column) columnIterator.next();
boolean useIt = revengStrategy.useColumnForOptimisticLock(identifier, column.getName());
if(useIt && !processed.contains(column)) {
bindVersionProperty( table, identifier, column, rc, processed, mapping );
return;
}
}
log.debug("No columns reported while scanning for / columns in " + identifier);
}
}
private void bindVersionProperty(Table table, TableIdentifier identifier, Column column, RootClass rc, Set processed, Mapping mapping) {
processed.add(column);
String propertyName = revengStrategy.columnToPropertyName( identifier, column.getName() );
Property property = bindBasicProperty(BinderUtils.makeUnique(rc, propertyName), table, column, processed, mapping);
rc.addProperty(property);
rc.setVersion(property);
rc.setOptimisticLockStyle(OptimisticLockStyle.VERSION);
log.debug("Column " + column.getName() + " will be used for / columns in " + identifier);
}
private Property bindBasicProperty(String propertyName, Table table, Column column, Set processedColumns, Mapping mapping) {
SimpleValue value = bindColumnToSimpleValue( table, column, mapping, false );
return PropertyBinder.makeProperty(
table,
defaultCatalog,
defaultSchema,
propertyName,
value,
true,
true,
false,
null,
null,
revengStrategy);
}
private SimpleValue bindColumnToSimpleValue(Table table, Column column, Mapping mapping, boolean generatedIdentifier) {
SimpleValue value = new SimpleValue((MetadataImplementor)metadata, table);
value.addColumn(column);
value.setTypeName(guessAndAlignType(table, column, mapping, generatedIdentifier));
return value;
}
/**
* @param columnIterator
* @param processedColumns
* @return
*/
private boolean contains(Iterator columnIterator, Set processedColumns) {
while (columnIterator.hasNext() ) {
Column element = (Column) columnIterator.next();
if(processedColumns.contains(element) ) {
return true;
}
}
return false;
}
private void checkColumn(Column column) {
if(column.getValue()!=null) {
//throw new JDBCBinderException("Binding column twice should not happen. " + column);
}
}
/**
* @param column
* @param generatedIdentifier
* @return
*/
private String guessAndAlignType(Table table, Column column, Mapping mapping, boolean generatedIdentifier) {
// TODO: this method mutates the column if the types does not match...not good.
// maybe we should copy the column instead before calling this method.
Integer sqlTypeCode = column.getSqlTypeCode();
String location =
"Table: " +
TableNameQualifier.qualify(table.getCatalog(), table.getSchema(), table.getQuotedName() ) +
" column: " +
column.getQuotedName();
if(sqlTypeCode==null) {
throw new JDBCBinderException("sqltype is null for " + location);
}
String preferredHibernateType = revengStrategy.columnToHibernateTypeName(
TableIdentifier.create(table),
column.getName(),
sqlTypeCode.intValue(),
column.getLength(), column.getPrecision(), column.getScale(), column.isNullable(), generatedIdentifier
);
Type wantedType = metadataCollector.getTypeResolver().heuristicType(preferredHibernateType);
if(wantedType!=null) {
int[] wantedSqlTypes = wantedType.sqlTypes(mapping);
if(wantedSqlTypes.length>1) {
throw new JDBCBinderException("The type " + preferredHibernateType + " found on " + location + " spans multiple columns. Only single column types allowed.");
}
int wantedSqlType = wantedSqlTypes[0];
if(wantedSqlType!=sqlTypeCode.intValue() ) {
log.debug("Sql type mismatch for " + location + " between DB and wanted hibernate type. Sql type set to " + typeCodeName( sqlTypeCode.intValue() ) + " instead of " + typeCodeName(wantedSqlType) );
column.setSqlTypeCode(new Integer(wantedSqlType));
}
}
else {
log.debug("No Hibernate type found for " + preferredHibernateType + ". Most likely cause is a missing UserType class.");
}
if(preferredHibernateType==null) {
throw new JDBCBinderException("Could not find javatype for " + typeCodeName(sqlTypeCode.intValue()));
}
return preferredHibernateType;
}
private String typeCodeName(int sqlTypeCode) {
return sqlTypeCode + "(" + JDBCToHibernateTypeHelper.getJDBCTypeName(sqlTypeCode) + ")";
}
/**
* Basically create an [classname]Id.class and add properties for it.
* @param rc
* @param compositeKeyColumns
* @param processed
* @return
*/
private SimpleValue handleCompositeKey(RootClass rc, Set processedColumns, List keyColumns, Mapping mapping) {
Component pkc = new Component((MetadataImplementor)metadata, rc);
pkc.setMetaAttributes(Collections.EMPTY_MAP);
pkc.setEmbedded(false);
String compositeIdName = revengStrategy.tableToCompositeIdName(TableIdentifier.create(rc.getTable()));
if(compositeIdName==null) {
compositeIdName = revengStrategy.classNameToCompositeIdName(rc.getClassName());
}
pkc.setComponentClassName(compositeIdName);
Table table = rc.getTable();
List list = null;
if (preferBasicCompositeIds ) {
list = new ArrayList(keyColumns);
}
else {
list = findForeignKeys(table.getForeignKeyIterator(), keyColumns);
}
for (Iterator> iter = list.iterator(); iter.hasNext();) {
Object element = iter.next();
Property property;
if (element instanceof Column) {
Column column = (Column) element;
if ( processedColumns.contains(column) ) {
throw new JDBCBinderException("Binding column twice for primary key should not happen: " + column);
}
else {
checkColumn(column);
String propertyName = revengStrategy.columnToPropertyName( TableIdentifier.create(table), column.getName() );
property = bindBasicProperty( BinderUtils.makeUnique(pkc, propertyName), table, column, processedColumns, mapping);
processedColumns.add(column);
}
}
else if (element instanceof ForeignKeyForColumns) {
ForeignKeyForColumns fkfc = (ForeignKeyForColumns) element;
ForeignKey foreignKey = fkfc.key;
String propertyName = revengStrategy.foreignKeyToEntityName(
foreignKey.getName(),
TableIdentifier.create(foreignKey.getTable() ),
foreignKey.getColumns(), TableIdentifier.create(foreignKey.getReferencedTable() ), foreignKey.getReferencedColumns(), true
);
property = bindManyToOne( BinderUtils.makeUnique(pkc, propertyName), true, table, foreignKey, processedColumns);
processedColumns.addAll(fkfc.columns);
}
else {
throw new JDBCBinderException("unknown thing");
}
markAsUseInEquals(property);
pkc.addProperty(property);
}
return pkc;
}
/**
* @param property
*/
private void markAsUseInEquals(Property property) {
Map m = new HashMap();
MetaAttribute ma = new MetaAttribute("use-in-equals");
ma.addValue("true");
m.put(ma.getName(),ma);
property.setMetaAttributes(m);
}
/**
* @param foreignKeyIterator
* @param columns
* @return
*/
private List findForeignKeys(Iterator> foreignKeyIterator, List pkColumns) {
List tempList = new ArrayList();
while(foreignKeyIterator.hasNext()) {
tempList.add((ForeignKey)foreignKeyIterator.next());
}
// Collections.reverse(tempList);
List result = new ArrayList();
Column myPkColumns[] = (Column[]) pkColumns.toArray(new Column[pkColumns.size()]);
for (int i = 0; i < myPkColumns.length; i++) {
boolean foundKey = false;
foreignKeyIterator = tempList.iterator();
while(foreignKeyIterator.hasNext()) {
ForeignKey key = (ForeignKey) foreignKeyIterator.next();
List matchingColumns = columnMatches(myPkColumns, i, key);
if(matchingColumns!=null) {
result.add(new ForeignKeyForColumns(key, matchingColumns));
i+=matchingColumns.size()-1;
foreignKeyIterator.remove();
foundKey=true;
break;
}
}
if(!foundKey) {
result.add(myPkColumns[i]);
}
}
return result;
}
private List columnMatches(Column[] myPkColumns, int offset, ForeignKey key) {
if(key.getColumnSpan()>(myPkColumns.length-offset)) {
return null; // not enough columns in the key
}
List columns = new ArrayList();
for (int j = 0; j < key.getColumnSpan(); j++) {
Column column = myPkColumns[j+offset];
if(!column.equals(key.getColumn(j))) {
return null;
} else {
columns.add(column);
}
}
return columns.isEmpty()?null:columns;
}
static class ForeignKeyForColumns {
protected final List columns;
protected final ForeignKey key;
public ForeignKeyForColumns(ForeignKey key, List columns) {
this.key = key;
this.columns = columns;
}
}
}