Hibernate5 dynamic model

原创
2023/06/20 16:32
阅读数 34

Hibernate dynamic model 动态模型

介绍

  Hibernate的动态模型为我们动态改动表结构带来了方便, 个人认为这一点非常有价值, 现在的企业级应用系统越来越强调用户可定制性, hibernate的这一点使用户自定义字段或自定义表成为可能 .

场景

  1. 启动时注册

    1. 使用 resources/hibernate/*.hbm.xml 文件的方式,在启动过程中,自动将类注册到 SessionFactory 中;
    2. 使用 Query query = session.createQuery("from DynamicTableColumn where tableName = '" + tableName + "'"); 进行查询。
  2. 运行时注册

    1. 容器启动完成后,程序正常运行期间产生的类,Hibernate 并不认识,所以需要进行注册;
    2. Hibernate 官方建议sessionFactory一旦创建好了,就不要对其做修改,所以如何对新创建的类进行管理和使用就很重要;
    3. 使用 Spring 提供的 LocalSessionFactoryBean 来得到SessionFactory;
    4. 详见 LocSessFacBeanConstant.java 类
      1. 定义了 locSessFacBeanList 对象用来存储运行时产生的各个不同的 SessionFactory;
      2. 定义了 get() 方法,用来获取 SessionFactory(支持操作启动时产生的类,也支持运行时产生的类);
package com.pap.jpa.constant;

import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.hibernate.tool.schema.TargetType;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;

import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.util.*;

/**
 * LocalSessionFactoryBean
 *
 *
 */
public class LocSessFacBeanConstant {

    /**
     * 存储动态添加的 LocalSessionFactoryBean 信息, 每一个动态模型对应一个 Map。
     */
    public static final List<Map<String, SessionFactory>> locSessFacBeanList = new ArrayList<Map<String, SessionFactory>>();

    /**
     * 获取 SessionFactory 
     * @param entityManager
     * @param dataSource
     * @param entityName
     * @param XML_MAPPING
     * @return
     * @throws Exception
     */
    public static SessionFactory get(EntityManager entityManager, DataSource dataSource, String entityName, String XML_MAPPING) throws Exception {
        SessionFactory sessionFactory = entityManager.getEntityManagerFactory().unwrap(SessionFactory.class);
//        ClassMetadata classMetadata = sessionFactory.getClassMetadata(entityName);
//        String[] propertyNames = classMetadata.getPropertyNames();

        IdentifierGenerator identifierGenerator = ((SessionFactoryImpl) sessionFactory).getIdentifierGenerator(entityName);
        if(identifierGenerator != null) {
            return sessionFactory;
        } else {
            for(Map<String, SessionFactory> localSessionFactoryBeanMap : LocSessFacBeanConstant.locSessFacBeanList) {
                if(localSessionFactoryBeanMap.containsKey(entityName)) {
                    return localSessionFactoryBeanMap.get(entityName);
                }
            }

            LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
            localSessionFactoryBean.setDataSource(dataSource);
            localSessionFactoryBean.afterPropertiesSet();

            Configuration configuration = localSessionFactoryBean.getConfiguration();
            ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();

            MetadataSources metadataSources = new MetadataSources(serviceRegistry);
            //读取映射文件
            metadataSources.addInputStream(new ByteArrayInputStream(XML_MAPPING.getBytes()));
            Metadata metadata = metadataSources.buildMetadata();
            //更新数据库Schema,如果不存在就创建表,存在就更新字段,不会影响已有数据
            SchemaUpdate schemaUpdate = new SchemaUpdate();
            schemaUpdate.execute(EnumSet.of(TargetType.DATABASE), metadata, serviceRegistry);

            //合并配置
            configuration.addInputStream(new ByteArrayInputStream(XML_MAPPING.getBytes()));
            SessionFactory newSessionFactory = configuration.buildSessionFactory(serviceRegistry);

            Map<String, SessionFactory> tmp = new HashMap<>();
            tmp.put(entityName, newSessionFactory);
            locSessFacBeanList.add(tmp);

            return newSessionFactory;
        }

    }
}

测试

  如 TestController 所示,假设在运行时定义了 XML_MAPPING 对象,根据 test() 方法,首先获取 SessionFactory 对象,并进行插入和查询操作

  1. LocSessFacBeanConstant.get() 方法,如果启动时产生的 SessionFactory 是否包含 Student 对象,不包含进入步骤2,否则直接返回 SessionFactory;
  2. 判断 LocSessFacBeanConstant.locSessFacBeanList 中是否已经生成了 Student 对象对应的 SessionFactory, 如果没有生成进入步骤3,否则直接返回 locSessFacBeanList 内指定的对象;
  3. 定义 LocalSessionFactoryBean 对象,并生成相关表结构,对配置进行合并,并将生成的 newSessionFactory 加入到 locSessFacBeanList 对象中,同时返回;

详见如下 TestController.java 文件。

package com.pap.jpa.controller;

import com.pap.jpa.constant.LocSessFacBeanConstant;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.util.*;
import java.util.List;
import java.util.Map;

@RestController
public class TestController {

    @Autowired
    private EntityManager entityManager;

    @Autowired
    private DataSource dataSource;

    public static final String XML_MAPPING = "";

    @GetMapping("/test")
    public void test(HttpServletRequest request) {
        try {
            SessionFactory newSessionFactory = LocSessFacBeanConstant.get(entityManager, dataSource, "Student", XML_MAPPING);
            //保存对象
            Session newSession = newSessionFactory.openSession();

            Map<String, Object> student = new HashMap<>();
            student.put("userName", "alexgaoyh");
            student.put("sex", "1");
            newSession.save("Student", student);

            newSession.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

参考

  1. https://www.jianshu.com/p/9fe01c47d3ff
  2. https://gitee.com/alexgaoyh/pap-jpa
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部