package org.hibernate.tool.hbmlint.detector;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.JDBCReaderFactory;
import org.hibernate.cfg.reveng.DatabaseCollector;
import org.hibernate.cfg.reveng.DefaultDatabaseCollector;
import org.hibernate.cfg.reveng.DefaultReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.JDBCReader;
import org.hibernate.cfg.reveng.JDBCToHibernateTypeHelper;
import org.hibernate.cfg.reveng.SchemaSelection;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.IdentifierCollection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Table;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.hbmlint.Issue;
import org.hibernate.tool.hbmlint.IssueCollector;
import org.hibernate.tool.util.TableNameQualifier;
public class SchemaByMetaDataDetector extends RelationalModelDetector {
public String getName() {
return "schema";
}
JDBCReader reader;
private TableSelectorStrategy tableSelector;
private DatabaseCollector dbc;
private Dialect dialect;
private Mapping mapping;
/** current table as read from the database */
Table currentDbTable = null;
public void initialize(Metadata metadata) {
super.initialize( metadata);
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder();
ServiceRegistry serviceRegistry = builder.build();
dialect = serviceRegistry.getService(JdbcServices.class).getDialect();
tableSelector = new TableSelectorStrategy(
new DefaultReverseEngineeringStrategy() );
reader = JDBCReaderFactory.newJDBCReader(
Environment.getProperties(),
tableSelector,
serviceRegistry);
dbc = new DefaultDatabaseCollector(reader.getMetaDataDialect());
}
public void visit(IssueCollector collector) {
super.visit(collector);
visitGenerators(collector);
}
public void visitGenerators(IssueCollector collector) {
Iterator<?> iter = iterateGenerators();
Set<?> sequences = Collections.EMPTY_SET;
if(dialect.supportsSequences()) {
sequences = reader.readSequences(dialect.getQuerySequencesString());
}
// TODO: move this check into something that could check per class or collection instead.
while ( iter.hasNext() ) {
PersistentIdentifierGenerator generator = (PersistentIdentifierGenerator) iter.next();
Object key = generator.generatorKey();
if ( !isSequence(key, sequences) && !isTable( key ) ) {
collector.reportIssue( new Issue( "MISSING_ID_GENERATOR", Issue.HIGH_PRIORITY, "Missing sequence or table: " + key));
}
}
}
private boolean isSequence(Object key, Set<?> sequences) {
if(key instanceof String) {
if ( sequences.contains( key ) ) {
return true;
} else {
String[] strings = StringHelper.split(".", (String) key);
if(strings.length==3) {
return sequences.contains(strings[2]);
} else if (strings.length==2) {
return sequences.contains(strings[1]);
}
}
}
return false;
}
private boolean isTable(Object key) throws HibernateException {
// BIG HACK - should probably utilize the table cache before going to the jdbcreader :(
if(key instanceof String) {
String[] strings = StringHelper.split(".", (String) key);
if(strings.length==1) {
tableSelector.clearSchemaSelections();
tableSelector.addSchemaSelection( new SchemaSelection(null,null, strings[0]) );
List<?> list = reader.readDatabaseSchema( dbc, null, null );
return !list.isEmpty();
} else if(strings.length==3) {
tableSelector.clearSchemaSelections();
tableSelector.addSchemaSelection( new SchemaSelection(strings[0],strings[1], strings[2]) );
List<?> list = reader.readDatabaseSchema( dbc, null, null );
return !list.isEmpty();
} else if (strings.length==2) {
tableSelector.clearSchemaSelections();
tableSelector.addSchemaSelection( new SchemaSelection(null,strings[0], strings[1]) );
List<?> list = reader.readDatabaseSchema( dbc, null, null );
return !list.isEmpty();
}
}
return false;
}
public void visit(Table table, IssueCollector pc) {
if ( table.isPhysicalTable() ) {
setSchemaSelection( table );
List<?> list = reader.readDatabaseSchema( dbc, null, null );
if ( list.isEmpty() ) {
pc.reportIssue( new Issue( "SCHEMA_TABLE_MISSING",
Issue.HIGH_PRIORITY, "Missing table "
+ TableNameQualifier.qualify( table.getCatalog(), table
.getSchema(), table.getName() ) ) );
return;
}
else if ( list.size() > 1 ) {
pc.reportIssue( new Issue( "SCHEMA_TABLE_MISSING",
Issue.NORMAL_PRIORITY, "Found "
+ list.size()
+ " tables for "
+ TableNameQualifier.qualify( table.getCatalog(), table
.getSchema(), table.getName() ) ) );
return;
}
else {
currentDbTable = (Table) list.get( 0 );
visitColumns(table,pc);
}
}
else {
// log?
}
}
String table(Table t) {
return TableNameQualifier.qualify( t.getCatalog(), t.getSchema(), t.getName() );
}
public void visit(
Table table,
Column col,
IssueCollector pc) {
if ( currentDbTable == null ) {
return;
}
Column dbColumn = currentDbTable
.getColumn( new Column( col.getName() ) );
if ( dbColumn == null ) {
pc.reportIssue( new Issue( "SCHEMA_COLUMN_MISSING",
Issue.HIGH_PRIORITY, table(table) + " is missing column: " + col.getName() ) );
}
else {
//TODO: this needs to be able to know if a type is truly compatible or not. Right now it requires an exact match.
//String sqlType = col.getSqlType( dialect, mapping );
int dbTypeCode = dbColumn.getSqlTypeCode().intValue();
int modelTypeCode = col
.getSqlTypeCode( mapping );
// TODO: sqltype name string
if ( !(dbTypeCode == modelTypeCode ) ) {
pc.reportIssue( new Issue( "SCHEMA_COLUMN_TYPE_MISMATCH",
Issue.NORMAL_PRIORITY, table(table) + " has a wrong column type for "
+ col.getName() + ", expected: "
+ JDBCToHibernateTypeHelper.getJDBCTypeName(modelTypeCode) + " but was " + JDBCToHibernateTypeHelper.getJDBCTypeName(dbTypeCode) + " in db") );
}
}
}
private void setSchemaSelection(Table table) {
tableSelector.clearSchemaSelections();
tableSelector.addSchemaSelection( new SchemaSelection( table
.getCatalog(), table.getSchema(), table.getName() ) );
}
/**
*
* @param cfg
* @return iterator over all the IdentifierGenerator's found in the entitymodel and return a list of unique IdentifierGenerators
* @throws MappingException
*/
@SuppressWarnings("deprecation")
private Iterator<IdentifierGenerator> iterateGenerators() throws MappingException {
TreeMap<Object, IdentifierGenerator> generators =
new TreeMap<Object, IdentifierGenerator>();
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder();
Properties properties = (Properties)builder.getSettings();
String defaultCatalog = properties.getProperty(AvailableSettings.DEFAULT_CATALOG);
String defaultSchema = properties.getProperty(AvailableSettings.DEFAULT_SCHEMA);
Iterator<PersistentClass> persistentClassIterator = getMetadata().getEntityBindings().iterator();
while ( persistentClassIterator.hasNext() ) {
PersistentClass pc = persistentClassIterator.next();
if ( !pc.isInherited() ) {
IdentifierGenerator ig = pc.getIdentifier()
.createIdentifierGenerator(
getMetadata().getIdentifierGeneratorFactory(),
dialect,
defaultCatalog,
defaultSchema,
(RootClass) pc
);
if ( ig instanceof PersistentIdentifierGenerator ) {
generators.put( ( (PersistentIdentifierGenerator) ig ).generatorKey(), ig );
}
}
}
Iterator<?> collectionIterator = getMetadata().getCollectionBindings().iterator();
while ( collectionIterator.hasNext() ) {
Collection collection = (Collection) collectionIterator.next();
if ( collection.isIdentified() ) {
IdentifierGenerator ig = ( (IdentifierCollection) collection ).getIdentifier()
.createIdentifierGenerator(
getMetadata().getIdentifierGeneratorFactory(),
dialect,
defaultCatalog,
defaultSchema,
null
);
if ( ig instanceof PersistentIdentifierGenerator ) {
generators.put( ( (PersistentIdentifierGenerator) ig ).generatorKey(), ig );
}
}
}
return generators.values().iterator();
}
}