笔记49 在Spittr应用中整合Hibernate
在前边构建的Spittr应用中整合Hibernate
由于最近所学的hibernate都是使用xml方式进行配置的,所以在与以Java方式配置的Spittr应用结合时就会出现一些小问题,在此进行总结。
一、读取上下文
因为原来的Spittr应用采用的是Java方式配置,没有用到xml。
DispatcherServlet是Spring MVC的核心。按照传统的方式,像DispatcherServlet这样的Servlet会配置在 web.xml文件中。但是在Spittr应用中,使用Java将DispatcherServlet配置在Servlet容器中,而没有使用web.xml文件。代码如下所示:
SpittrWebAppInitializer.java
1 package myspittr.config; 2 3 import javax.servlet.MultipartConfigElement; 4 import javax.servlet.ServletRegistration.Dynamic; 5 6 import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; 7 8 public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { 9 @Override 10 protected Class<?>[] getRootConfigClasses() { 11 // TODO Auto-generated method stub 12 return new Class<?>[] { RootConfig.class }; 13 } 14 15 @Override 16 protected Class<?>[] getServletConfigClasses() { 17 // TODO Auto-generated method stub 18 return new Class<?>[] { WebConfig.class }; 19 } 20 21 @Override 22 protected String[] getServletMappings() { 23 // TODO Auto-generated method stub 24 return new String[] { "/" }; 25 } 26 27 @Override 28 protected void customizeRegistration(Dynamic registration) { 29 // TODO Auto-generated method stub 30 registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads", 2097152, 4194304, 0)); 31 } 32 33 }
WebConfig.java
1 package myspittr.config; 2 3 import java.io.IOException; 4 5 import org.springframework.context.MessageSource; 6 import org.springframework.context.annotation.Bean; 7 import org.springframework.context.annotation.ComponentScan; 8 import org.springframework.context.annotation.Configuration; 9 import org.springframework.context.support.ResourceBundleMessageSource; 10 import org.springframework.web.multipart.MultipartResolver; 11 import org.springframework.web.multipart.support.StandardServletMultipartResolver; 12 import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; 13 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 14 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 15 import org.springframework.web.servlet.view.InternalResourceViewResolver; 16 17 @Configuration("WebConfig") 18 @EnableWebMvc 19 @ComponentScan({ "myspittr.web", "myspittr.data" }) 20 public class WebConfig extends WebMvcConfigurerAdapter { 21 @Bean 22 public InternalResourceViewResolver viewResolver() { 23 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 24 resolver.setPrefix("/WEB-INF/views/"); 25 resolver.setSuffix(".jsp"); 26 resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class); 27 resolver.setExposeContextBeansAsAttributes(true); 28 return resolver; 29 } 30 31 @Override 32 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 33 // TODO Auto-generated method stub 34 configurer.enable(); 35 } 36 37 @Bean 38 public MessageSource messageSource() { 39 ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); 40 messageSource.setBasename("message"); 41 return messageSource; 42 } 43 44 @Bean 45 public MultipartResolver multipartResolver() throws IOException { 46 return new StandardServletMultipartResolver(); 47 } 48 49 // @Bean 50 // public MultipartResolver multipartResolver() throws IOException { 51 // return new CommonsMultipartResolver(); 52 // } 53 }
RootConfig.java
1 package myspittr.config; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.ComponentScan.Filter; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.context.annotation.FilterType; 7 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 8 9 @Configuration("RootConfig") 10 @ComponentScan(basePackages = { "myspittr" }, excludeFilters = { 11 @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class) }) 12 public class RootConfig { 13 14 }
AbstractAnnotationConfigDispatcherServletInitializer的任意类都会自动地配置Dispatcher-Servlet和Spring应用上下文,Spring的应用上下文会位于应用程序的Servlet上下文之中。
插入语:
在Servlet 3.0环境 中,容器会在类路径中查找实现 javax.servlet.ServletContainerInitializer接口的类, 如果能发现的话,就会用它来配置Servlet容器。 Spring提供了这个接口的实现,名 为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给 它们来完成。Spring 3.2引入了一个便利的 WebApplicationInitializer基础实现,也就是AbstractAnnotationConfigDispatcherServletInitializer 因为我们的Spittr-WebAppInitializer扩展了 AbstractAnnotationConfig DispatcherServletInitializer(同时也就实现了 WebApplicationInitializer),因此当部署到Servlet 3.0容器中的时候,容器会自动发现它,并用它来配置Servlet上下文。
尽管它的名字很长,但 是AbstractAnnotationConfigDispatcherServletInitializer使用起来很简便。在上述代码中,SpittrWebAppInitializer重写了三个方法。 第一个方法是getServletMappings(),它会将一个或多个路径映 射到DispatcherServlet上。在本例中,它映射的是“/”,这表示 它会是应用的默认Servlet。它会处理进入应用的所有请求。 为了理解其他的两个方法,我们首先要理解DispatcherServlet 和一个Servlet监听器(也就是ContextLoaderListener)的关系。
两个应用(Spring和SpringMVC)上下文之间的关系:
当DispatcherServlet启动的时候,它会创建Spring应用上下文,并加载配置文件或配置类中所声明的bean。在上述代码中的 getServletConfigClasses()方法中,我们要求DispatcherServlet加载应用上下文时,使用定义在WebConfig配置类(使用Java配置)中的bean。 但是在Spring Web应用中,通常还会有另外一个应用上下文。另外的这个应用上下文是由ContextLoaderListener创建的。 我们希望DispatcherServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射,而ContextLoaderListener要加载应用中的其他bean。这些bean通常是驱动应用后端的中间层和数据层组件。
实际上,AbstractAnnotationConfigDispatcherServletInitializer 会同时创建DispatcherServlet和 ContextLoaderListener。GetServlet-ConfigClasses() 方法返回的带有@Configuration注解的类将会用来定 义DispatcherServlet应用上下文中的 bean。getRootConfigClasses()方法返回的带 有@Configuration注解的类将会用来配 置ContextLoaderListener创建的应用上下文中的bean。 在本例中,根配置定义在RootConfig中,DispatcherServlet 的配置声明在WebConfig中。需要注意的是,通过 AbstractAnnotationConfigDispatcherServletInitializer来配置DispatcherServlet是传统web.xml方式的替代方案。如果你愿意的话,可以同时包含web.xml和 AbstractAnnotationConfigDispatcherServletInitializer,但这其实并没有必要。
但是,问题是现在还不会使用Java方式对hibernate进行配置,所以没有办法只能采用xml方式配置。如果使用Java方式整合hibernate,那么将hibernate相关的bean放入WebConfig.java中即可。
需要注意的是,以xml方式整合hibernate时,需要在web.xml中进行如下配置:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 5 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" id="WebApp_1529217958650"> 6 <!-- 配置Spring IOC 容器 --> 7 <context-param> 8 <param-name>contextConfigLocation</param-name> 9 <param-value>classpath:applicationContext.xml</param-value> 10 </context-param> 11 <listener> 12 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 13 </listener> 14 15 <!-- 配置Springmvc的核心控制器 --> 16 <servlet> 17 <servlet-name>dispatcherServlet</servlet-name> 18 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 19 <init-param> 20 <param-name>contextConfigLocation</param-name> 21 <param-value>classpath:springmvc.xml</param-value> 22 </init-param> 23 <load-on-startup>1</load-on-startup> 24 </servlet> 25 <servlet-mapping> 26 <servlet-name>dispatcherServlet</servlet-name> 27 <url-pattern>/*</url-pattern> 28 </servlet-mapping> 29 <!-- 配置编码方式过滤器,且必须配置在所有的过滤器最前面 --> 30 <filter> 31 <filter-name>characterEncodingFilter</filter-name> 32 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 33 <init-param> 34 <param-name>encoding</param-name> 35 <param-value>UTF-8</param-value> 36 </init-param> 37 </filter> 38 <filter-mapping> 39 <filter-name>characterEncodingFilter</filter-name> 40 <url-pattern>/*</url-pattern> 41 </filter-mapping> 42 <!-- 为了使用SpringMVC框架实现REST,需配置HiddenHttpMethodFilter --> 43 <filter> 44 <filter-name>hiddenHttpMethodFilter</filter-name> 45 <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> 46 </filter> 47 <filter-mapping> 48 <filter-name>hiddenHttpMethodFilter</filter-name> 49 <url-pattern>/*</url-pattern> 50 </filter-mapping> 51 52 </web-app>
但是其中DispatcherServlet和 ContextLoaderListener已经在SpittrWebAppInitializer.java中配置过了,所以要去掉。那么如何加载hibernate相关的bean呢?新建applicationContext.xml文件,在这里面存放有关hibernate的配置,具体代码如下所示:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 6 http://www.springframework.org/schema/context 7 http://www.springframework.org/schema/context/spring-context-4.1.xsd"> 8 9 <!-- 配置数据源 --> 10 <context:property-placeholder location="classpath:/config/db.properties" /> 11 12 <!-- 配置DataSource --> 13 <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> 14 <property name="user" value="${jdbc.user}"></property> 15 <property name="password" value="${jdbc.password}"></property> 16 <property name="driverClass" value="${jdbc.driverClass}"></property> 17 <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> 18 </bean> 19 20 <!-- 配置SeeionFactory --> 21 <bean class="org.springframework.orm.hibernate5.LocalSessionFactoryBean" 22 id="sessionFactory"> 23 <!-- 配置数据源 --> 24 <property name="dataSource" ref="dataSource"></property> 25 <!-- 配置实体包(pojo) --> 26 <property name="PhysicalNamingStrategy"> 27 <bean 28 class="org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl"></bean> 29 </property> 30 <property name="packagesToScan" value="com.Spittr.entity"></property> 31 32 <!-- 配置Hibernate的常用属性 --> 33 <property name="hibernateProperties"> 34 <props> 35 <!-- 数据库的方言 --> 36 <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> 37 <prop key="hibernate.show_sql">true</prop> 38 <prop key="hibernate.format_sql">true</prop> 39 <prop key="hibernate.hbm2ddl.auto">update</prop> 40 </props> 41 </property> 42 </bean> 43 44 <!-- 配置hibernate的事务管理器 --> 45 <bean class="org.springframework.orm.hibernate5.HibernateTransactionManager"> 46 <property name="sessionFactory" ref="sessionFactory"></property> 47 </bean> 48 </beans>
如何加载这里面的bean呢?使用ClassPathXmlApplicationContext获取配置文件,然后使用getBean方法获取相应bean,即SessionFactory。
代码如下:
1 ApplicationContext ctx= new ClassPathXmlApplicationContext("/config/applicationContext.xml"); 2 SessionFactory sessionFactory=ctx.getBean(SessionFactory.class);
classpath请参考:笔记48
二、Spittr应用中数据的处理
1.应用中只涉及到两个实体类,一个是spiitle,另一个是spitter。它俩之间的关系是:一个spitter可以对应多个spittle,多个spittle对应一个spitter。
所以在spittle中增加下面代码:
1 private Spitters spitters; 2 @ManyToOne 3 @JoinColumn(name = "author") 4 public Spitters getSpitters() { 5 return spitters; 6 } 7 8 public void setSpitters(Spitters spitters) { 9 this.spitters = spitters; 10 }
在spitter中增加下面代码:
1 Set<Spittles> spittles; 2 @OneToMany(fetch = FetchType.EAGER) 3 @JoinColumn(name = "author") 4 public Set<Spittles> getSpittles() { 5 return spittles; 6 } 7 8 public void setSpittles(Set<Spittles> spittles) { 9 this.spittles = spittles; 10 }
2.Spittr中注册界面和发布页面分别对应实体类spitter和spittle,但是如果这两个实体类直接使用hibernate映射为数据库中的表时,会出现一些问题,因为有一些字段是不用存储在数据库当中的,比如说确认密码。所以需要新建两个实体类,用作与数据库的交互,而原来的spitter和spittle用来与应用交互。下面给出两个新的实体类:
Spitters.java
1 package com.Spittr.entity; 2 3 import java.io.Serializable; 4 import java.util.Set; 5 6 import javax.persistence.Entity; 7 import javax.persistence.FetchType; 8 import javax.persistence.GeneratedValue; 9 import javax.persistence.GenerationType; 10 import javax.persistence.Id; 11 import javax.persistence.JoinColumn; 12 import javax.persistence.OneToMany; 13 import javax.persistence.Table; 14 15 @Entity 16 @Table(name = "spitters") 17 public class Spitters implements Serializable { 18 /** 19 * 20 */ 21 private static final long serialVersionUID = 1L; 22 23 int id; 24 String username; 25 String password; 26 String email; 27 Set<Spittles> spittles; 28 29 public Spitters() { 30 // TODO Auto-generated constructor stub 31 } 32 33 public Spitters(String username, String password, String email) { 34 super(); 35 this.username = username; 36 this.password = password; 37 this.email = email; 38 } 39 40 @Id 41 @GeneratedValue(strategy = GenerationType.IDENTITY) 42 public int getId() { 43 return id; 44 } 45 46 public void setId(int id) { 47 this.id = id; 48 } 49 50 public String getUsername() { 51 return username; 52 } 53 54 public void setUsername(String username) { 55 this.username = username; 56 } 57 58 public String getPassword() { 59 return password; 60 } 61 62 public void setPassword(String password) { 63 this.password = password; 64 } 65 66 public String getEmail() { 67 return email; 68 } 69 70 public void setEmail(String email) { 71 this.email = email; 72 } 73 74 @OneToMany(fetch = FetchType.EAGER) 75 @JoinColumn(name = "author") 76 public Set<Spittles> getSpittles() { 77 return spittles; 78 } 79 80 public void setSpittles(Set<Spittles> spittles) { 81 this.spittles = spittles; 82 } 83 84 }
Spittles.java
1 package com.Spittr.entity; 2 3 import java.io.Serializable; 4 5 import javax.persistence.Entity; 6 import javax.persistence.GeneratedValue; 7 import javax.persistence.GenerationType; 8 import javax.persistence.Id; 9 import javax.persistence.JoinColumn; 10 import javax.persistence.ManyToOne; 11 import javax.persistence.Table; 12 13 @Entity 14 @Table(name = "spittles") 15 public class Spittles implements Serializable { 16 /** 17 * 18 */ 19 private static final long serialVersionUID = 1L; 20 private int id; 21 private Long spittleId; 22 private String message; 23 private String title; 24 private String time; 25 private String username; 26 private String spittlePicture; 27 28 private Spitters spitters; 29 30 public Spittles() { 31 // TODO Auto-generated constructor stub 32 } 33 34 public Spittles(Long spittleId, String message, String title, String time, String username, String spittlePicture) { 35 this.spittleId = spittleId; 36 this.message = message; 37 this.title = title; 38 this.time = time; 39 this.username = username; 40 this.spittlePicture = spittlePicture; 41 } 42 43 @Id 44 @GeneratedValue(strategy = GenerationType.IDENTITY) 45 public int getId() { 46 return id; 47 } 48 49 public void setId(int id) { 50 this.id = id; 51 } 52 53 public Long getSpittleId() { 54 return spittleId; 55 } 56 57 public void setSpittleId(Long spittleId) { 58 this.spittleId = spittleId; 59 } 60 61 public String getMessage() { 62 return message; 63 } 64 65 public void setMessage(String message) { 66 this.message = message; 67 } 68 69 public String getTitle() { 70 return title; 71 } 72 73 public void setTitle(String title) { 74 this.title = title; 75 } 76 77 public String getTime() { 78 return time; 79 } 80 81 public void setTime(String time) { 82 this.time = time; 83 } 84 85 public String getUsername() { 86 return username; 87 } 88 89 public void setUsername(String username) { 90 this.username = username; 91 } 92 93 public String getSpittlePicture() { 94 return spittlePicture; 95 } 96 97 public void setSpittlePicture(String spittlePicture) { 98 this.spittlePicture = spittlePicture; 99 } 100 101 @ManyToOne 102 @JoinColumn(name = "author") 103 public Spitters getSpitters() { 104 return spitters; 105 } 106 107 public void setSpitters(Spitters spitters) { 108 this.spitters = spitters; 109 } 110 111 }
三、总结