package org.aksw.sparqlify.jpa; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.aksw.sparqlify.algebra.sql.exprs2.S_ColumnRef; import org.aksw.sparqlify.core.cast.SqlValue; import org.aksw.sparqlify.inverse.SparqlSqlInverseMap; import org.aksw.sparqlify.inverse.SparqlSqlInverseMapper; import org.aksw.sparqlify.util.SqlOpUtils; import org.hibernate.SessionFactory; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.entity.AbstractEntityPersister; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.jena.sparql.core.Quad; public class EntityInverseMapperImplHibernate implements EntityInverseMapper { private static final Logger logger = LoggerFactory.getLogger(EntityInverseMapperImplHibernate.class); private SparqlSqlInverseMapper inverseMapper; private Map<String, AbstractEntityPersister> tableNameToPersister; public EntityInverseMapperImplHibernate(SparqlSqlInverseMapper inverseMapper, Map<String, AbstractEntityPersister> tableNameToPersister) { this.inverseMapper = inverseMapper; this.tableNameToPersister = tableNameToPersister; } @Override public List<EntityRef> map(Quad quad) { List<SparqlSqlInverseMap> invMaps = inverseMapper.map(quad); List<EntityRef> result = new ArrayList<EntityRef>(invMaps.size()); for(SparqlSqlInverseMap invMap : invMaps) { EntityRef entityRef = map(invMap); if(entityRef != null) { result.add(entityRef); } } return result; } public EntityRef map(SparqlSqlInverseMap invMap) { EntityRef result = map(invMap, tableNameToPersister); return result; } public static EntityRef map(SparqlSqlInverseMap invMap, Map<String, AbstractEntityPersister> metadata) { String tableName = SqlOpUtils.getTableName(invMap.getViewDefinition().getMapping().getSqlOp()); EntityRef result = null; if(tableName != null) { Map<String, Object> columnToValue = makeSimple(invMap.getColumnToValue()); result = map(tableName, columnToValue, metadata); } return result; } // public static EntityInverseMapperImplHibernate create(SparqlSqlInverseMapper inverseMapper, EntityManagerFactory emf) { // SessionFactory sessionFactory = ((HibernateEntityManagerFactory)emf).getSessionFactory(); public static EntityInverseMapperImplHibernate create(SparqlSqlInverseMapper inverseMapper, SessionFactory sessionFactory) { Map<String, AbstractEntityPersister> tableNameToPersister = createTablePersisterMap(sessionFactory); EntityInverseMapperImplHibernate result = new EntityInverseMapperImplHibernate(inverseMapper, tableNameToPersister); return result; } public static Map<String, Object> makeSimple(Map<S_ColumnRef, SqlValue> map) { Map<String, Object> result = new HashMap<String, Object>(); for(Entry<S_ColumnRef, SqlValue> entry : map.entrySet()) { S_ColumnRef colRef = entry.getKey(); SqlValue sqlValue = entry.getValue(); String columnName = colRef.getColumnName(); Object value = sqlValue.getValue(); result.put(columnName, value); } return result; } public static EntityRef map(String tableName, Map<String, Object> columnConstraints, Map<String, AbstractEntityPersister> metadata) { AbstractEntityPersister persister = metadata.get(tableName); if(persister == null) { return null; } //TODO Only works on entity ids yet, however should be extended to work on arbitrary properties Map<String, String> columnToProperty = new HashMap<String, String>(); String[] propertyNames = persister.getPropertyNames(); for(String propertyName : propertyNames) { //String propertyTableName = persister.getPropertyTableName(propertyName); //System.out.println(propertyTableName); String[] columnNames = persister.getPropertyColumnNames(propertyName); if(columnNames.length > 1 || columnNames.length == 0) { logger.warn("Skipped property with multi column mapping: " + propertyName + " -> " + columnNames); // Skip multi column properties for now continue; } String columnName = columnNames[0]; // TODO Non-portable code (cross-dbms) I don't get how to get the final SQL column names from hibernate :/ // The ones I get here are camel case, where as in the db they are all lower case columnName = columnName.toLowerCase(); columnToProperty.put(columnName, propertyName); } // Set the id property (may override a prior mapping) // TODO This code may not be robust String idPropertyName = persister.getIdentifierPropertyName(); String[] idColumnNames = persister.getIdentifierColumnNames(); if (idColumnNames.length == 1) { String idColumnName = idColumnNames[0]; columnToProperty.put(idColumnName, idPropertyName); } Map<String, Object> propertyToValue = new HashMap<String, Object>(); boolean allMatch = true; for(Entry<String, Object> constraint : columnConstraints.entrySet()) { String columnName = constraint.getKey(); Object value = constraint.getValue(); String propertyName = columnToProperty.get(columnName); if(propertyName == null) { allMatch = false; break; } propertyToValue.put(propertyName, value); //System.out.println("TODO How to deal with compound keys? How to map column names to properties? Probably we have to iterate all the properties and see whether the columns contribute to it"); //persister.getPropert } Class<?> entityClass = persister.getMappedClass(); EntityRef result = allMatch ? new EntityRef(entityClass, propertyToValue) : null; return result; } /** * * * @param sessionFactory * @return A collection of AbstractEntityPersister objects indexed by their table name */ public static Map<String, AbstractEntityPersister> createTablePersisterMap(SessionFactory sessionFactory) { Map<String, AbstractEntityPersister> result = new HashMap<String, AbstractEntityPersister>(); Collection<ClassMetadata>classMetadatas = sessionFactory.getAllClassMetadata().values(); for(ClassMetadata classMetadata : classMetadatas) { if (!(classMetadata instanceof AbstractEntityPersister)) { continue; } AbstractEntityPersister persister = (AbstractEntityPersister)classMetadata; String tmpTableName = persister.getTableName(); String tableName = tmpTableName.toLowerCase(); logger.warn("[HACK] Converted table name '" + tmpTableName +"' to '" + tableName + "', but this should be done via the SQL dialect or something"); //tableName = tableName.toLowerCase(); result.put(tableName, persister); } return result; } /** * Look up an instance of a class by a URI * * TODO We would need access to the candidate selector here, in order to pick the view definitions for a specific table * * * @param clazz * @param uri * @return */ /* public <T> T find(Class<T> clazz, String uri) { for(AbstractEntityPersister persister : tableNameToPersister.values()) { Class<?> mappedClazz = persister.getMappedClass(); if(clazz.isAssignableFrom(mappedClazz)) { inverseMapper.map(quad) } } ClassMetadata classMetadata = sessionFactory.getClassMetadata(entityClass); //classMetadata. return null; } */ }