JTable Spring Hibernate 演示





5.00/5 (4投票s)
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