Обновление Hibernate до 5.2 — создание фабрики сеансов и замена PersistentClass для получения свойств класса сущностей

В настоящее время я обновляю свою версию Hibernate до последней версии 5.2.10. Я заменил свой код в HibernateUtil для создания SessionFactory.

4.3.11.Final (предыдущий):

 public class HibernateUtil {
   private HibernateUtil() {}

   private static SessionFactory sessionFactory;

    private static Configuration configuration;

    public static Configuration getConfiguration() {
        return configuration;
    }
    private static SessionFactory buildSessionFactory() {
        try {
                     if(sessionFactory == null) {
                        configuration = new Configuration();
                        configuration.configure("hibernate.cfg.xml");
                        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
                                .applySettings(configuration.getProperties()).build();
                        sessionFactory = configuration
                                .buildSessionFactory(serviceRegistry);
                     }
            return sessionFactory;
        }
        catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }

    }
    public static SessionFactory getSessionFactory() {
        return buildSessionFactory();
    }

    public static Session getSession() {
        Session hibernateSession = getSessionFactory().getCurrentSession();
        return hibernateSession;
      }

    public static void shutdown() {
       getSessionFactory().close();
    }
}

5.2.10 Финал (новый):

public class HibernateUtil {
  private static StandardServiceRegistry registry;
  private static SessionFactory sessionFactory;

   public static SessionFactory getSessionFactory() {
        return buildSessionFactory();
   }

  public static SessionFactory buildSessionFactory() {
    if (sessionFactory == null) {
      try {
        registry = new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();
        MetadataSources sources = new MetadataSources(registry);
        Metadata metadata = sources.getMetadataBuilder().build();
        sessionFactory = metadata.getSessionFactoryBuilder().build();
      } catch (Exception e) {
        e.printStackTrace();
        shutdown();
      }
    }
    return sessionFactory;
  }

public static Session getSession() {
        Session hibernateSession = getSessionFactory().getCurrentSession();
        return hibernateSession;
      }

  public static void shutdown() {
    if (registry != null) {
      StandardServiceRegistryBuilder.destroy(registry);
    }
  }
}

Теперь у меня есть метод, который будет получать список имен столбцов, передавая имя таблицы БД в виде строки. Я делал это раньше в 4.3.11.Final:

public static List<String> getColumnNames(String tableName) {

        List<String> columnList=null;

        Map<String, ClassMetadata> map = HibernateUtil.getSessionFactory().getAllClassMetadata();
        Iterator<Entry<String, ClassMetadata>>  itr =  map.entrySet().iterator();

        while(itr.hasNext()){

            ClassMetadata classMetaData = itr.next().getValue();
            AbstractEntityPersister aep = (AbstractEntityPersister) classMetaData;

            if(aep.getTableName().split("\\.")[1].equalsIgnoreCase(tableName)){

                columnList = new ArrayList<String>();
                String[] propertyNames = classMetaData.getPropertyNames();

                for(String property : propertyNames){
                        try {
                            PersistentClass persistentClass = HibernateUtil .getConfiguration().getClassMapping(classMetaData.getEntityName());
                            String clmName =  ((Column) persistentClass.getProperty(property).getColumnIterator().next()).getName();
                            columnList.add(clmName);
                        } catch(NoSuchElementException e){
                            log.error("Element not found idenfied as : "+property);
                        } catch(Exception e){
                            log.error(e.getMessage());
                        }
                }
                break;
            }
        }

        return columnList;
    }

Теперь, после обновления, он показывает метод getAllClassMetadata как устаревший, и я сталкиваюсь с трудностями при получении объекта PersistentClass. Я видел аналогичный вопрос здесь, но я не мог точно найти решение. Какую часть моего текущего кода мне нужно изменить, чтобы мой метод getColumnNames() работал точно так же, как раньше. Я сослался на документацию, и в ней говорится, что вместо этого следует использовать EntityManagerFactory.getMetamodel(), но я не могу найти подходящие справочные примеры того же самого. Также мне придется изменить механизм создания SessionFactory для этого?


person Hasan K    schedule 25.04.2017    source источник


Ответы (4)


Во-первых, нам нужно создать новый MetadataExtractorIntegrator, реализующий Hibernate Integrator:

public class MetadataExtractorIntegrator 
    implements org.hibernate.integrator.spi.Integrator {

    public static final MetadataExtractorIntegrator INSTANCE = 
        new MetadataExtractorIntegrator();

    private Database database;

    @Override
    public void integrate(
            Metadata metadata,
            SessionFactoryImplementor sessionFactory,
            SessionFactoryServiceRegistry serviceRegistry) {

        database = metadata.getDatabase();
    }

    @Override
    public void disintegrate(
        SessionFactoryImplementor sessionFactory,
        SessionFactoryServiceRegistry serviceRegistry) {

    }

    public Database getDatabase() {
        return database;
    }
}

Затем мы можем просто настроить Hibernate для его использования.

Если вы используете механизм начальной загрузки Hibernate, вы можете добавить его следующим образом:

final BootstrapServiceRegistryBuilder bsrb = new BootstrapServiceRegistryBuilder();
bsrb.enableAutoClose();

Integrator integrator = integrator();
if (integrator != null) {
    bsrb.applyIntegrator( integrator );
}

final BootstrapServiceRegistry bsr = bsrb.build();

final StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder(bsr);

Если вы загружаетесь с помощью JPA, вы можете сделать это следующим образом:

protected EntityManagerFactory newEntityManagerFactory() {
    PersistenceUnitInfo persistenceUnitInfo = persistenceUnitInfo(
        getClass().getSimpleName()
    );
    
    Map<String, Object> configuration = new HashMap<>();
    
    
    configuration.put("hibernate.integrator_provider", 
        (IntegratorProvider) () -> Collections.singletonList( MetadataExtractorIntegrator.INSTANCE )
    );

    EntityManagerFactoryBuilderImpl entityManagerFactoryBuilder = new EntityManagerFactoryBuilderImpl(
            new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration
    );
    return entityManagerFactoryBuilder.build();
}

Теперь при запуске следующего теста:

for(Namespace namespace : MetadataExtractorIntegrator.INSTANCE
    .getDatabase()
    .getNamespaces()) {
     
    for( Table table : namespace.getTables()) {
        LOGGER.info( "Table {} has the following columns: {}",
             table,
             StreamSupport.stream(
                Spliterators.spliteratorUnknownSize( 
                    table.getColumnIterator(), 
                    Spliterator.ORDERED
                ), 
                false
            )
            .collect( Collectors.toList()) 
        );
    }
}

Hibernate выводит в лог все отображаемые на данный момент таблицы:

Table org.hibernate.mapping.Table(post) has the following columns: [
    org.hibernate.mapping.Column(id), 
    org.hibernate.mapping.Column(title), 
    org.hibernate.mapping.Column(version)
]
Table org.hibernate.mapping.Table(post_comment) has the following columns: [
    org.hibernate.mapping.Column(id), 
    org.hibernate.mapping.Column(review), 
    org.hibernate.mapping.Column(version), 
    org.hibernate.mapping.Column(post_id)
]
Table org.hibernate.mapping.Table(post_details) has the following columns: [
    org.hibernate.mapping.Column(id), 
    org.hibernate.mapping.Column(created_by), 
    org.hibernate.mapping.Column(created_on), 
    org.hibernate.mapping.Column(version)
]
Table org.hibernate.mapping.Table(post_tag) has the following columns: [
    org.hibernate.mapping.Column(post_id), 
    org.hibernate.mapping.Column(tag_id)
]
Table org.hibernate.mapping.Table(tag) has the following columns: [
    org.hibernate.mapping.Column(id), 
    org.hibernate.mapping.Column(name), 
    org.hibernate.mapping.Column(version)
]

Вот и все!

person Vlad Mihalcea    schedule 01.05.2017
comment
Спасибо @Vlad за ваш ответ. Итак, мне придется заменить мой код класса HibernateUtil, который я упомянул выше, на механизм начальной загрузки Hibernate, который вы предложили? Также не могли бы вы объяснить немного больше об этом, так как этот механизм является относительно новым для меня. Как он считывает конфигурацию как der no hibernate.cfg.xml в вашем коде. Строка Integrator integrator = integrator() относится к какому методу интегратора? Также в тестовом методе namespace.getTables() является частью org.hibernate.boot.relational.Namespace. Также как я могу получить имя столбца? - person Hasan K; 02.05.2017

Ну наконец-то я это сделал благодаря статье Влада. Я взял код интегратора без каких-либо изменений и изменил свои методы HibernateUtil и getColumns(). Итак, вот мой код:

Создание SessionFactory:

public class HibernateUtil {

    private static final SessionFactory sessionFactory = buildSessionFactory();

    public static SessionFactory getSessionFactory() {
        return buildSessionFactory();
    }

    private static SessionFactory buildSessionFactory() {
        final BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder().enableAutoClose()
                .applyIntegrator(MetadataExtractorIntegrator.INSTANCE).build();

        final StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder(bootstrapServiceRegistry).configure().build();
        return new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory();
    }

    public static Session getSession() {
        Session hibernateSession = getSessionFactory().getCurrentSession();
        return hibernateSession;
    }

    public static void shutdown() {
        getSessionFactory().close();
    }
}

Извлечение метаданных (получение имен столбцов):

public static List<String> getColumnNames(String tableName) {

    List<String> columnList = new ArrayList<>();

    for (Namespace namespace : MetadataExtractorIntegrator.INSTANCE.getDatabase().getNamespaces()) {
        for (Table table : namespace.getTables()) {
            if (table.getName().equalsIgnoreCase(lookupTableName)) {
                Iterator<Column> iterator = table.getColumnIterator();
                while (iterator.hasNext()) {
                    columnList.add(iterator.next().getName());
                }
                break;
            }
        }
        if (!columnList.isEmpty())
            break;
    }
    return columnList;
}
person Hasan K    schedule 06.05.2017

Начиная с Hibernate — 5.4.30.Final — выпущен 19 марта 2021 г.

Приведенный ниже код работает и не устарел:

MetamodelImplementor metaModelImpl = (MetamodelImplementor)session.getMetamodel();
Map<String, EntityPersister> entityPersisters = metaModelImpl.entityPersisters();
Collection<EntityPersister> val = entityPersisters.values();               

for (EntityPersister ep : val) {
        AbstractEntityPersister aep = (AbstractEntityPersister)ep;

        System.out.println(aep.getTableName());
        System.out.println(Arrays.toString(aep.getIdentifierColumnNames()));
        for (String propName : aep.getPropertyNames()) {
               System.out.println(propName);
               System.out.println(Arrays.toString(aep.getPropertyColumnNames(propName)));
        }
 }
person M. Amer    schedule 18.04.2021

Более простой способ получить имя столбца с помощью hibernate 5

final AbstractEntityPersister classMetadata = (AbstractEntityPersister) sessionFactory.getClassMetadata(clazz)
final String[] names = classMetadata.getPropertyColumnNames(property)
person jpozorio    schedule 05.04.2018
comment
Метод Hibernate-SessionFactory getClassMetadata также устарел. - person olivmir; 17.09.2018
comment
Не соответствует документам hibernate docs.jboss.org/hibernate/ orm/5.1/javadocs/org/hibernate/ - person jpozorio; 24.09.2018
comment
В 5.1 еще нет, но начиная с 5.2 он устарел (вопрос касался версии 5.2.10): docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/ - person olivmir; 24.09.2018