65.9K
CodeProject 正在变化。 阅读更多。
Home

JTable Spring Hibernate 演示

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2016年7月19日

CPOL

4分钟阅读

viewsIcon

17696

Spring Hibernate 集成以及 JTable 小部件。

目录

  • 引言
  • Hibernate 配置
  • 定义拦截器
  • 拦截器需求
  • 使用拦截器
  • 创建 Hibernate Factory
  • 销毁 Hibernate Factory
  • 模型
  • 控制器 (Controller)
  • 使用 Hibernate 进行数据库操作
  • 使用 Hibernate 查询数据库

引言

本文与 JTable Spring 演示相同(https://codeproject.org.cn/Articles/1112115/JTable-Spring-Demo-with-database-integration)。在此演示中,使用 Hibernate 代替 Spring JdbcTemplate 进行数据库访问。

Hibernate 配置

Hibernate 需要一组属性来配置。配置在 hibernate.cfg.xml 中,如下所示

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
       <property name="hibernate.dialect">
            org.hibernate.dialect.HSQLDialect
        </property>
        <property name="hibernate.connection.driver_class">
            org.hsqldb.jdbcDriver
        </property>
        <property name="hibernate.connection.url"> 
   jdbc:hsqldb:file:C:\DB\StudentDB;shutdown=true;hsqldb.default_table_type=cached;sql.enforce_strict_size=false
        </property>
        <property name="hibernate.connection.username">
            sa
        </property>
        <property name="hibernate.connection.password">
        </property>
    </session-factory>
</hibernate-configuration>

有关更多详细信息,请参阅 https://tutorialspoint.org.cn/hibernate/hibernate_configuration.htm

定义拦截器                          

Spring Interceptor 用于拦截请求并按需进行一些处理,然后将其移交给 Controller 以满足请求。为了做到这一点,创建了一个 SpringRequestInterceptor 对象并将其添加到注册表中。拦截器在 SpringConfiguration.java 中定义如下

@Import({SpringSecurityConfig.class})
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"controller"})

public class SpringConfiguration extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        SpringRequestInterceptor interceptor = new SpringRequestInterceptor();
        registry.addInterceptor(interceptor);
    }
}

拦截器需求

Spring 提供了 HttpServletRequest、HttpServletResponse、HttpSession 和 ServletContext 对象。创建 Hibernate session 需要这些对象。但是,为了使用这些对象,需要将它们传递给一个方法,然后根据 Spring 的要求使用。由于代码中有各种需要这些对象的方法,为了避免将它们连续地作为参数传递,因此使用了拦截器。在处理任何 HTTP 请求之前,会调用拦截器,拦截器会调用所需的方法来存储对象并按需关闭 session。

使用拦截器

可以通过覆盖抽象类 HandlerInterceptorAdapter 的方法或通过编写 HandlerInterceptor 接口来创建拦截器。抽象类提供了 HandlerInterceptor 接口的基本实现。这里覆盖了 HandlerInterceptorAdapter 的两个方法(preHandle() 和 postHandle())。

当调用拦截器时,SpringRequestInterceptor.java 检查 handler 是否指向 BaseController。如果是,则在处理任何请求之前和之后分别调用 BaseController 上的 preHandle() 和 postHandle() 方法。

public class SpringRequestInterceptor extends HandlerInterceptorAdapter {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Object bean = handlerMethod.getBean();
            if (bean instanceof BaseController) {
                BaseController controller = (BaseController) bean;
               return controller.preHandle(request, response, handler);
            }
        }
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Object bean = handlerMethod.getBean();
            if (bean instanceof BaseController) {
                BaseController controller = (BaseController) bean;
                controller.postHandle(request, response, handler, modelAndView);
            }
        }
    }

对于任何 HTTP 请求,在处理之前,会调用 BaseController 中定义的拦截器的 preHandle() 方法。HttpServletRequest、HttpServletResponse、HttpSession 和 ServletContext 对象会传递给它并存储起来以备将来使用。postHandle() 方法用于在处理请求后关闭 Hibernate session。

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // Store request, response, httpSession and httpContext. Will be useful later.
    this.request = request;
    this.response = response;
    httpSession = request.getSession(true);
    httpContext = request.getServletContext();
    session = null;
    return true;
}
// Closing the hibernate session after processing the request.
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    try {
        session.close();
        session = null;
        request = null;
        response = null;
        httpSession = null;
        httpContext = null;
    } 
catch (Exception ignore) {
    }
}

创建 Hibernate Factory

下面的代码片段展示了如何创建 Factory。Factory 的创建非常耗时且开销很大,不建议为每个请求都创建它,因为它会使应用程序变慢。Factory 只创建一次,存储在应用程序中,并在需要时使用。

为了只创建一次 Factory,在 Factory 首次创建并存储在 ServletContext 中后,会设置 SessionFactory 属性。然后每次都会检查 SessionFactory 属性。如果存在,则使用已有的;否则,会创建新的 Factory。

.addAnnotatedClasses 用于将类映射到数据库。

BaseController.java 的 getFactory()

public static SessionFactory getFactory(ServletContext httpContext) {
    SessionFactory factory = (SessionFactory) httpContext.getAttribute("SessionFactory");
    if (factory != null) return factory;
    StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
    registryBuilder.configure();
    StandardServiceRegistry registry = registryBuilder.build();

    MetadataSources metadataSources = new MetadataSources(registry);   
    metadataSources.addAnnotatedClass(City.class);
    metadataSources.addAnnotatedClass(Course.class);
    metadataSources.addAnnotatedClass(Student.class);
    metadataSources.addAnnotatedClass(StudentPhone.class);
    metadataSources.addAnnotatedClass(StudentResult.class);
    metadataSources.addAnnotatedClass(User.class);

    Metadata metadata = metadataSources.buildMetadata();
    factory = metadata.buildSessionFactory();
    httpContext.setAttribute("SessionFactory", factory);
    return factory;
}

销毁 Hibernate Factory

Factory 需要在应用程序关闭时销毁。ServletContext 是应用程序的公共区域。Factory 存储在 ServletContext 中。当 context 被销毁时,如果 factory 存在,它也会被销毁。属性“SessionFactory”也会被移除。Annotation @WebListener 使一个类成为 ServletContextListener(contextInitialized 和 contextDestroyed 方法会被调用)。

SpringContextListener.java 的 contextDestroyed()

@Override

public void contextDestroyed(ServletContextEvent event) {
    ServletContext httpContext = event.getServletContext();
    SessionFactory factory = (SessionFactory) httpContext.getAttribute("SessionFactory");
    if (factory != null) {
        factory.close();
        httpContext.removeAttribute("SessionFactory");
    }
}

模型

创建 Student.java 以映射到 Student 表。@Entity 和 @Table 注解用于指示创建“student”表。@Id、@GeneratedValue 和 @Column 注解用于指示为主键列创建自增主键。代码片段仅显示了几个字段。实际代码请参考源代码中的 Student.java。

Student.java

@Entity
@Table(name = "student")

public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    public int id;
    
    @NotNull
    @Size(min = 2, max = 30)
    public String name;

    @NotNull
    @Email
    public String email;

    @NotNull
    public String password;
}

类似地,为以下表创建了表:

  • City,包含 id、name 字段。
  • StudentResults,包含 id、student_id、course_name、exam_date、degree 字段。
  • StudentPhone,包含 id、student_id、phone_type、phone_number、record_date 字段。
  • Course,包含 id、name 字段。

所有类都添加了 getter 和 setter 方法。jTable 支持主/子表(http://jtable.org/Demo/MasterChild)。StudentPhone 和 StudentResults 是为子表定义的。

控制器 (Controller)

每个 controller 都定义了三个操作:List、Save、Delete。

StudentController.java

List()

@ResponseBody
@RequestMapping(value = "List")

public JTableResult List(JTableRequest jTableRequest) {
    JTableResult rslt = new JTableResult();
    try {
        createSession();
        return Student.retrievePage(session, jTableRequest);
    } catch (Throwable ex) {
        rslt.Result = "Error";
        rslt.Message = exceptionToString(ex);
        return rslt;
    }
}

RetrievePage 方法将根据分页和排序参数返回记录。

保存()

@ResponseBody
@RequestMapping(value = "Save")

public JTableResult Save(@Valid Student student, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) return toError(bindingResult);
    int action = Integer.parseInt(request.getParameter("action"));
    if (student.active_flg == null) student.active_flg = "N";
    if (action == 1) {
        student.record_date = new Date();
        return insert(student);
    } else {
        createSession();
        student.record_date = Student.getRecordDateById(session, student.id);
        return update(student);
    }
}

getrecorddatebyid() 方法获取正在更新的记录的原始 record_date。Save 方法根据传递的 action 参数值调用 BaseController.java 的 insert 或 update 方法。

Delete()

@ResponseBody
@RequestMapping(value = "Delete")

public JTableResult Delete(int id) {
    Student student = new Student();
    student.id = id;
    return delete(student);
}

调用 BaseController.java 的 Delete 方法。

使用 Hibernate 进行数据库操作

对于每个数据库操作,都会创建一个 session。事务开始后,执行所需的操作(例如,保存、更新或删除),然后提交操作。请求处理完毕后,postHandle 方法会关闭 session。

BaseController.java

使用 Hibernate 向数据库插入记录。

public JTableResult insert(Object obj) {
    JTableResult rslt = new JTableResult();
    try {
        //Hibernate session is created for database access.
        createSession();
        session.getTransaction().begin();
        session.save(obj);
        session.getTransaction().commit();
        rslt.Result = "OK";
        rslt.Record = obj;
        return rslt;
    } catch (Throwable ex) {
        rslt.Result = "Error";
        rslt.Message = exceptionToString(ex);
        return rslt;
    }

使用 .update(obj) 和 .delete(obj) 也可以类似地更新和删除数据库中的记录。

使用 Hibernate 查询数据库

为了获取每个表的总记录数,执行了一个查询。

Student.java 的 retrievePage() 按如下方式获取总记录数:

rslt.TotalRecordCount = ((BigInteger) session.createNativeQuery("Select count(*) From student").getSingleResult()).intValue()

.createNativeQuery 允许查询数据库。获得的查询结果可以根据查询的“Select”部分和返回的记录数进行存储。

源代码

代码可在以下位置获取: https://github.com/pujagani/JTableSpringHibernateDemo

 

© . All rights reserved.