SHARE
TWEET

Untitled

a guest Mar 22nd, 2019 111 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. package com.github.manevolent.jbot.database;
  2.  
  3. import com.google.common.collect.MapMaker;
  4. import org.hibernate.*;
  5. import org.hibernate.boot.model.naming.*;
  6. import org.hibernate.cfg.Configuration;
  7. import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
  8. import org.hibernate.type.Type;
  9.  
  10. import javax.persistence.EntityManager;
  11. import java.io.Serializable;
  12. import java.util.*;
  13. import java.util.function.Function;
  14. import java.util.stream.Collectors;
  15.  
  16. public class HibernateManager implements DatabaseManager {
  17.     private static final String tableNamingFormat = "%s_%s";
  18.  
  19.     private final Properties properties;
  20.  
  21.     private final Object entityLock = new Object();
  22.     private final Map<String, EntityMapping> entityByName = new LinkedHashMap<>();
  23.     private final Set<EntityMapping> entities = new LinkedHashSet<>();
  24.     private final Map<String, com.github.manevolent.jbot.database.Database> databases = new LinkedHashMap<>();
  25.  
  26.     /**
  27.      * This naming strategy allows tables to be implicitly named via a globally-acceptable naming format
  28.      * (see tableNamingFormat)
  29.      *
  30.      * This way, tables can be named uniquely per database.
  31.      */
  32.     private final ImplicitNamingStrategy implicitNamingStrategy = new ImplicitNamingStrategyJpaCompliantImpl() {
  33.         public Identifier determinePrimaryTableName(ImplicitEntityNameSource source) {
  34.             Identifier identifier = super.determinePrimaryTableName(source);
  35.  
  36.             EntityMapping entityMapping = entityByName.get(source.getEntityNaming().getClassName());
  37.             if (entityMapping == null) throw new IllegalArgumentException(source.getEntityNaming().getClassName());
  38.  
  39.             // Build new identifier
  40.             return new Identifier(
  41.                     String.format(tableNamingFormat, entityMapping.database.getName(), identifier.getText()),
  42.                     identifier.isQuoted()
  43.             );
  44.         }
  45.     };
  46.  
  47.     /**
  48.      * Physical naming strategy is not controlled
  49.      */
  50.     private final PhysicalNamingStrategy physicalNamingStrategy = new PhysicalNamingStrategyStandardImpl() {
  51.         public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
  52.             return super.toPhysicalTableName(name, context);
  53.         }
  54.     };
  55.  
  56.     /**
  57.      * Interceptor provides global (cross-SessionFactory) caching mechanism for the system
  58.      */
  59.     private final Interceptor interceptor = new EmptyInterceptor() {
  60.         @Override
  61.         public boolean onLoad(Object entity, Serializable key, Object[] values, String[] properties, Type[] types)
  62.                 throws CallbackException {
  63.             Class<?> clazz = entity.getClass();
  64.             EntityMapping mapping = entityByName.get(clazz.getName());
  65.             if (mapping == null) return false;
  66.  
  67.             mapping.putInstance(key, entity);
  68.             return true;
  69.         }
  70.  
  71.         @Override
  72.         public Object getEntity(String entityName, Serializable id) {
  73.             EntityMapping mapping = entityByName.get(entityName);
  74.             if (mapping == null) return null;
  75.  
  76.             return entityByName.get(entityName).getInstance(id);
  77.         }
  78.     };
  79.  
  80.     public HibernateManager(Properties properties) {
  81.         this.properties = new Properties();
  82.  
  83.         for (String property : properties.stringPropertyNames())
  84.             this.properties.setProperty(property, properties.getProperty(property));
  85.  
  86.         this.properties.setProperty("hibernate.enable_lazy_load_no_trans", "true");
  87.     }
  88.  
  89.     public Collection<Class<?>> getEntities() {
  90.         return Collections.unmodifiableCollection(
  91.                 entities.stream()
  92.                 .map(EntityMapping::getEntityClass)
  93.                 .collect(Collectors.toList())
  94.         );
  95.     }
  96.  
  97.     private EntityMapping registerEntityClass(Database database, Class<?> clazz) {
  98.         synchronized (entityLock) {
  99.             EntityMapping mapping = new EntityMapping(clazz, database);
  100.  
  101.             entities.add(mapping);
  102.             entityByName.put(clazz.getName(), mapping);
  103.  
  104.             return mapping;
  105.         }
  106.     }
  107.  
  108.     /**
  109.      * Builds a new SessionFactory given the specific graph objects.
  110.      * @return SessionFactory instance.
  111.      */
  112.     private SessionFactory buildFactory(Collection<Class<?>> classes) {
  113.         Configuration configuration = new Configuration();
  114.  
  115.         configuration.addProperties(properties);
  116.  
  117.         configuration.setPhysicalNamingStrategy(physicalNamingStrategy);
  118.         configuration.setImplicitNamingStrategy(implicitNamingStrategy);
  119.         configuration.setInterceptor(interceptor);
  120.  
  121.         classes.forEach(configuration::addAnnotatedClass);
  122.  
  123.         return configuration.buildSessionFactory();
  124.     }
  125.  
  126.     @Override
  127.     public Collection<com.github.manevolent.jbot.database.Database> getDatabases() {
  128.         return Collections.unmodifiableCollection(databases.values());
  129.     }
  130.  
  131.     @Override
  132.     public com.github.manevolent.jbot.database.Database defineDatabase(String name,
  133.                                    Function<Database.ModelConstructor,
  134.                                            com.github.manevolent.jbot.database.Database> function) {
  135.         return databases.computeIfAbsent(name, key -> {
  136.             Database.ModelConstructor constructor = new com.github.manevolent.jbot.database.Database.ModelConstructor()
  137.             {
  138.                 private final Set<com.github.manevolent.jbot.database.Database> dependentDatabases
  139.                         = new LinkedHashSet<>();
  140.  
  141.                 /**
  142.                  * All entities that must be accessible by this database, including self entities as defined below.
  143.                  */
  144.                 private final Set<Class<?>> allEntities = new LinkedHashSet<>();
  145.  
  146.                 /**
  147.                  * Entities specific to this database at the dependency node level
  148.                  */
  149.                 private final Set<Class<?>> selfEntities = new LinkedHashSet<>();
  150.  
  151.                 @Override
  152.                 public String getDatabaseName() {
  153.                     return key;
  154.                 }
  155.  
  156.                 /**
  157.                  * Recursive function used to obtain all needed entity class definitions for this dependency tree
  158.                  * @param database database to depend
  159.                  */
  160.                 private void dependIntl(com.github.manevolent.jbot.database.Database database) {
  161.                     if (!(database instanceof Database))
  162.                         throw new IllegalArgumentException(
  163.                                 "database",
  164.                                 new ClassCastException(
  165.                                         databases.getClass().getName() +
  166.                                                 " cannot be cast to " +
  167.                                                 Database.class.getName()));
  168.  
  169.                     if (((Database) database).instance != HibernateManager.this) {
  170.                         throw new IllegalArgumentException(
  171.                                 "database",
  172.                                 new IllegalAccessException(
  173.                                         database.getClass().getName()
  174.                                                 + " was created by a different instance of " +
  175.                                                 HibernateManager.class.getName()));
  176.                     }
  177.  
  178.                     // depend on database self entities in the respective tree for this database
  179.                     ((Database) database).selfEntities.forEach(this::registerDependentEntity);
  180.  
  181.                     // depend on this database's children, as well.
  182.                     for (com.github.manevolent.jbot.database.Database child :
  183.                             ((Database) database).dependentDatabases)
  184.                         dependIntl(child);
  185.                 }
  186.  
  187.                 @Override
  188.                 public Database.ModelConstructor depend(com.github.manevolent.jbot.database.Database database) {
  189.                     dependIntl(database);
  190.  
  191.                     dependentDatabases.add(database);
  192.  
  193.                     return this;
  194.                 }
  195.  
  196.                 private void registerDependentEntity(Class<?> aClass) {
  197.                     // recognize in the large list
  198.                     if (!allEntities.add(aClass))
  199.                         throw new IllegalStateException("entity class " + aClass.getName() + " already registered");
  200.                 }
  201.  
  202.                 private void registerSelfEntity(Class<?> aClass) {
  203.                     // recognize in self list (used for future dependency graphing)
  204.                     if (!selfEntities.add(aClass))
  205.                         throw new IllegalStateException("entity class " + aClass.getName() + " already registered");
  206.                 }
  207.  
  208.                 @Override
  209.                 public Database.ModelConstructor registerEntity(Class<?> aClass) {
  210.                     registerDependentEntity(aClass); // done for HashSet duplication checking
  211.  
  212.                     registerSelfEntity(aClass); // done for future dependency resolution
  213.  
  214.                     return this;
  215.                 }
  216.  
  217.                 @Override
  218.                 public Database define() {
  219.                     return new Database(name, selfEntities, allEntities, dependentDatabases);
  220.                 }
  221.             };
  222.  
  223.             return function.apply(constructor);
  224.         });
  225.     }
  226.  
  227.     private class Database implements com.github.manevolent.jbot.database.Database {
  228.         private final HibernateManager instance = HibernateManager.this;
  229.  
  230.         private final String name;
  231.  
  232.         private final Collection<EntityMapping> selfMappings = new LinkedHashSet<>();
  233.         private final Collection<Class<?>> selfEntities;
  234.         private final Collection<Class<?>> allEntities;
  235.         private final Collection<com.github.manevolent.jbot.database.Database> dependentDatabases;
  236.  
  237.         private final SessionFactory sessionFactory;
  238.  
  239.         public Database(String name,
  240.                         Collection<Class<?>> selfEntities,
  241.                         Collection<Class<?>> allEntities,
  242.                         Collection<com.github.manevolent.jbot.database.Database> dependentDatabases) {
  243.             this.name = name;
  244.  
  245.             this.selfEntities = selfEntities;
  246.             this.allEntities = allEntities;
  247.             this.dependentDatabases = dependentDatabases;
  248.  
  249.             this.sessionFactory = buildSessionFactory();
  250.         }
  251.  
  252.         private SessionFactory buildSessionFactory() {
  253.             // register own entities
  254.             selfEntities.forEach(clazz -> selfMappings.add(registerEntityClass(this, clazz)));
  255.  
  256.             // build the SessionFactory used to interact with this model graph
  257.             return buildFactory(allEntities);
  258.         }
  259.  
  260.         @Override
  261.         public String getName() {
  262.             return name;
  263.         }
  264.  
  265.         @Override
  266.         public Collection<Class<?>> getEntities() {
  267.             return Collections.unmodifiableCollection(selfEntities);
  268.         }
  269.  
  270.         @Override
  271.         public Collection<com.github.manevolent.jbot.database.Database> getDependentDatabases() {
  272.             return Collections.unmodifiableCollection(dependentDatabases);
  273.         }
  274.  
  275.         @Override
  276.         public boolean isClosed() {
  277.             return sessionFactory.isClosed();
  278.         }
  279.  
  280.         @Override
  281.         public EntityManager openSession() {
  282.             return sessionFactory.openSession();
  283.         }
  284.  
  285.         @Override
  286.         public int hashCode() {
  287.             return getName().hashCode();
  288.         }
  289.  
  290.         @Override
  291.         public void close() {
  292.             sessionFactory.close();
  293.  
  294.             for (EntityMapping mapping : selfMappings)
  295.                 mapping.clearPersistence();
  296.         }
  297.     }
  298.  
  299.     private class EntityMapping {
  300.         private final Class<?> clazz;
  301.         private final Database database;
  302.         private final Map<Serializable, Object> persistenceMap = new MapMaker().weakValues().makeMap();
  303.  
  304.         private EntityMapping(Class<?> clazz, Database database) {
  305.             this.clazz = clazz;
  306.             this.database = database;
  307.         }
  308.  
  309.         public Class<?> getEntityClass() {
  310.             return clazz;
  311.         }
  312.  
  313.         public Database getDatabase() {
  314.             return database;
  315.         }
  316.  
  317.         public void clearPersistence() {
  318.             persistenceMap.clear();
  319.         }
  320.  
  321.         public Object getInstance(Serializable key) {
  322.             return persistenceMap.get(key);
  323.         }
  324.  
  325.         public Object putInstance(Serializable key, Object instance) {
  326.             return persistenceMap.put(key, instance);
  327.         }
  328.  
  329.         @Override
  330.         public int hashCode() {
  331.             return clazz.hashCode() ^ database.hashCode();
  332.         }
  333.     }
  334. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top