Hibernate
随着应用程序变得越来越复杂,对持久化的需求也变得更复杂。我们需要将对象的属性映射到数据库的列上,并且需要自动生成语句和查询,这样我们就能从无休止的问号字符串中解脱出来。此外,我们还需要一些更复杂的特性:
Spring对多个持久化框架都提供了支持,包括Hibernate、iBATIS、Java数据对象(Java Data Objects,JDO)以及Java持久化API(Java Persistence API,JPA)。与Spring对JDBC的支持那样,Spring对ORM框架的支持提供了与这些框架的集成点以及一些附加的服务:
11.1 在Spring中集成Hibernate
Hibernate是在开发者社区很流行的开源持久化框架。它不仅提供了基本的对象关系映射,还提供了ORM工具所应具有的所有复杂功能,比如缓存、延迟加载、预先抓取以及分布式缓存。
使用Hibernate所需的主要接口是org.hibernate.Session。Session接口提供了基本的数据访问功能,如保存、更新、删除以及从数据库加载对象的功能。通过Hibernate的Session接口,应用程序的Repository能够满足所有的持久化需求。
获取Hibernate Session对象的标准方式是借助于Hibernate Session Factory接口的实现类。SessionFactory主要负责Hibernate Session的打开、关闭以及管理。
在Spring中,我们要通过Spring的某一个Hibernate Session工厂bean来获取Hibernate SessionFactory。从3.1版本开始,Spring提供了三个Session工厂bean供我们选择:
至于选择哪一个Session工厂,取决于使用哪一个版本的Hibernate以及使用XML还是使用注解定义对象-数据库之间的映射关系。
对于Spring3,如果使用XML配置,就使用LocalSessionFactoryBean;如果使用Java注解,则使用AnnotationSessionFactoryBean。
对于Hibernate 4,那么就应该使 用org.springframework.orm.hibernate4中的 LocalSessionFactoryBean。尽管它与Hibernate 3包中的 LocalSessionFactoryBean使用了相同的名称,但是Spring 3.1新引入的这个Session工厂类似于Hibernate 3中 LocalSessionFactoryBean和 AnnotationSessionFactoryBean的结合体。它有很多相同的属性,能够支持基于XML的映射和基于注解的映射。
LocalSessionFactoryBean实现org.springframework.beans.factory.FactoryBean接口, spring在装配的时候, 如果发现实现了org.springframework.beans.factory.FactoryBean接口, 就会使用FactoryBean#getObject() 方法返回的对象装配。spring返回的不是LocalSessionFactoryBean 本身,他会自动调用getObject()这个方法,把真正的session factory返回。
如下的代码展现了 如何对它进行配置,使其支持基于注解的映射:
开启事务管理
Spring会自动开启关闭事务,这就需要声明事务。这种申明式事务的好处是,把事务的管理完全从业务程序中分离出来,作为一个可配置、可插拔的功能。程序中不再需要任何管理事务的代码,既可以完全实现事务处理的功能。
声明事务管理器配置:
@EnableTransactionManagement通知Spring,@Transactional注解的类被事务的切面包围。这样@Transactional就可以使用了。完整的配置如下:
清单 DefaultAppConfig.java:
需求分析
类似于用户及博客的关系,一个Spitter可以发多条Spittle,Spittles与Spitter是多对一的关系。
Spring Security权限检查复用Spitter表。
程序流程为:
为了使Spitter符合Spring Security规范,需添加两个字段role(用户角色)、enabled(是否开启权限控制)。
清单1 Spitter.java
清单2 Spittle.java
清单3 RootConfig.java:
清单4 SecurityConfig.java:
清单5 Role.java:
清单7 SpittleRepository.java:
11.1.2 构建不依赖于Spring的Hibernate代码
现在的最佳实践是不再使用HibernateTemplate,而是使用上下文Session(Contextual session)。通过这种方式,会直接将Hibernate SessionFactory装配到Repository中,并使用它来获取Session,如下面的程序清单所示。
清单 HibernateSpittlerRepository.java:
如果抛出异常:
org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister
请检查类的setter/getter方法拼写是否符合规范
如果抛出异常:
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
需添加@Transactional
执行结果:
(1) 访问主页:
(2)访问注册页面,并输入数据,提交
(3)注册成功
(4)点击Spittles链接
(5)输入文本,点击添加,进入权限验证页面:
用新注册的号码验证成功后,重新进入Spittles发文:
- 延迟加载(Lazy loading):随着我们的对象关系变得越来越复杂,有时候并不希望立即获取完整的对象间关系。举一个典型的例子,假设在查询一组PurchaseOrder对象,而每个对象中都包含一个LineItem对象集合。如果只关心PurchaseOrder的属性,那查询出LineItem的数据就毫无意义。而且这可能是开销很大的操作。延迟加载允许只在需要的时候获取数据。
- 预先抓取(Eager fetching):这与延迟加载是相对的。借助于预先抓取,我们可以使用一个查询获取完整的关联对象。如果我们需要PurchaseOrder及其关联的LineItem对象,预先抓取的功能可以在一个操作中将它们全部从数据库中取出来,节省了多次查询的成本。
- 级联(Cascading):有时,更改数据库中的表会同时修改其他表。回到我们订购单的例子中,当删除Order对象时,我们希望同时在数据库中删除关联的LineItem。
Spring对多个持久化框架都提供了支持,包括Hibernate、iBATIS、Java数据对象(Java Data Objects,JDO)以及Java持久化API(Java Persistence API,JPA)。与Spring对JDBC的支持那样,Spring对ORM框架的支持提供了与这些框架的集成点以及一些附加的服务:
- 支持集成Spring声明式事务;
- 透明的异常处理;
- 线程安全的、轻量级的模板类;
- DAO支持类;
- 资源管理。
11.1 在Spring中集成Hibernate
Hibernate是在开发者社区很流行的开源持久化框架。它不仅提供了基本的对象关系映射,还提供了ORM工具所应具有的所有复杂功能,比如缓存、延迟加载、预先抓取以及分布式缓存。
使用Hibernate所需的主要接口是org.hibernate.Session。Session接口提供了基本的数据访问功能,如保存、更新、删除以及从数据库加载对象的功能。通过Hibernate的Session接口,应用程序的Repository能够满足所有的持久化需求。
获取Hibernate Session对象的标准方式是借助于Hibernate Session Factory接口的实现类。SessionFactory主要负责Hibernate Session的打开、关闭以及管理。
在Spring中,我们要通过Spring的某一个Hibernate Session工厂bean来获取Hibernate SessionFactory。从3.1版本开始,Spring提供了三个Session工厂bean供我们选择:
- org.springframework.orm.hibernate3.LocalSessionFactoryBean
- org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean
- org.springframework.orm.hibernate4.LocalSessionFactoryBean
至于选择哪一个Session工厂,取决于使用哪一个版本的Hibernate以及使用XML还是使用注解定义对象-数据库之间的映射关系。
对于Spring3,如果使用XML配置,就使用LocalSessionFactoryBean;如果使用Java注解,则使用AnnotationSessionFactoryBean。
对于Hibernate 4,那么就应该使 用org.springframework.orm.hibernate4中的 LocalSessionFactoryBean。尽管它与Hibernate 3包中的 LocalSessionFactoryBean使用了相同的名称,但是Spring 3.1新引入的这个Session工厂类似于Hibernate 3中 LocalSessionFactoryBean和 AnnotationSessionFactoryBean的结合体。它有很多相同的属性,能够支持基于XML的映射和基于注解的映射。
LocalSessionFactoryBean实现org.springframework.beans.factory.FactoryBean接口, spring在装配的时候, 如果发现实现了org.springframework.beans.factory.FactoryBean接口, 就会使用FactoryBean#getObject() 方法返回的对象装配。spring返回的不是LocalSessionFactoryBean 本身,他会自动调用getObject()这个方法,把真正的session factory返回。
如下的代码展现了 如何对它进行配置,使其支持基于注解的映射:
@Bean
public LocalSessionFactoryBean sessionFactory() {
System.out.println("hibernate自动设置数据源");
LocalSessionFactoryBean sfb = new LocalSessionFactoryBean();
//设置数据源
sfb.setDataSource(dataSource);
//使用packagesToScan属性告诉Spring扫描一个或多个包以查找域类,这些类通过注解的方式表明要使用
//Hibernate进行持久化,这些类可以使用的注解包括JPA的@Entity
//或@MappedSuperclass以及Hibernate的@Entity。
sfb.setPackagesToScan("spittr");
Properties props = new Properties();
//SQL dialect 指定使用MySQL数据库格式的SQL语句
props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
//是否在控制台打印生成的SQL语句
props.setProperty("hibernate.show_sql", "true");
//输出格式化后的sql,更方便查看
props.setProperty("hibernate.format_sql", "false");
//指定Hibernate启动的时候自动创建或更新表结构
props.setProperty("hibernate.hbm2ddl.auto", "update");
//关闭beanvalitionFactory自动验证
props.setProperty("javax.persistence.validation.mode", "none");
sfb.setHibernateProperties(props);
return sfb;
}
dataSource和hibernateProperties属性都声明了从哪里获取数据库连接以及要使用哪一种数据库。这里不再列出Hibernate配置文件,而是使用packagesToScan属性告诉Spring扫描一个或多个包以查找域类,这些类通过注解的方式表明要使用 Hibernate进行持久化,这些类可以使用的注解包括JPA的@Entity 或@MappedSuperclass以及Hibernate的@Entity。
开启事务管理
Spring会自动开启关闭事务,这就需要声明事务。这种申明式事务的好处是,把事务的管理完全从业务程序中分离出来,作为一个可配置、可插拔的功能。程序中不再需要任何管理事务的代码,既可以完全实现事务处理的功能。
声明事务管理器配置:
//事务管理器配置 @Bean public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) { HibernateTransactionManager txManager = new HibernateTransactionManager(); txManager.setSessionFactory(sessionFactory); return txManager; }然后使用@EnableTransactionManagement注解开启注解式事务的支持。
@EnableTransactionManagement通知Spring,@Transactional注解的类被事务的切面包围。这样@Transactional就可以使用了。完整的配置如下:
清单 DefaultAppConfig.java:
package spittr.config; import java.util.Properties; import javax.sql.DataSource; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.orm.hibernate4.HibernateTransactionManager; import org.springframework.orm.hibernate4.LocalSessionFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * Spring4基于注解的配置类, 用于代替原来的<b>applicationContext.xml配置文件 * @author lenovo * */ @Configuration @Import(DataSourceConfig.class) //导入spring jdbc配置 //开启事务管理,proxyTargetClass= true代表开启类的事务管理 @EnableTransactionManagement(proxyTargetClass = true) public class DefaultAppConfig { @Autowired private DataSource dataSource; @Bean public LocalSessionFactoryBean sessionFactory() { System.out.println("hibernate自动设置数据源"); LocalSessionFactoryBean sfb = new LocalSessionFactoryBean(); //设置数据源 sfb.setDataSource(dataSource); //使用packagesToScan属性告诉Spring扫描一个或多个包以查找域类,这些类通过注解的方式表明要使用 //Hibernate进行持久化,这些类可以使用的注解包括JPA的@Entity或@MappedSuperclass以及Hibernate的@Entity。 sfb.setPackagesToScan("spittr"); Properties props = new Properties(); //SQL dialect 指定使用MySQL数据库格式的SQL语句 props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect"); //是否在控制台打印生成的SQL语句 props.setProperty("hibernate.show_sql", "true"); //输出格式化后的sql,更方便查看 props.setProperty("hibernate.format_sql", "false"); //指定Hibernate启动的时候自动创建或更新表结构 props.setProperty("hibernate.hbm2ddl.auto", "update"); //关闭beanvalitionFactory自动验证 props.setProperty("javax.persistence.validation.mode", "none"); sfb.setHibernateProperties(props); return sfb; } //事务管理器配置 @Bean public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) { HibernateTransactionManager txManager = new HibernateTransactionManager(); txManager.setSessionFactory(sessionFactory); return txManager; } }
package spittr.config; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; /** * 数据源配置类 * @author lenovo * */ @Configuration public class DataSourceConfig { @Bean(destroyMethod="shutdown") @Profile("dev") public DataSource dataSource() { System.out.println("初始化数据库"); return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .setScriptEncoding("UTF-8") .build(); } @Bean(destroyMethod="close") @Profile("prod") public DataSource mysqlDataSource() { BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/spittr?characterEncoding=UTF-8"); ds.setUsername("root"); ds.setPassword("root"); ds.setInitialSize(5); ds.setMaxActive(10); return ds; } }
需求分析
类似于用户及博客的关系,一个Spitter可以发多条Spittle,Spittles与Spitter是多对一的关系。
Spring Security权限检查复用Spitter表。
程序流程为:
![SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客](http://img2.ph.126.net/ACKp8zMuD-GT2XMRvGsXGA==/1423981907279514845.png)
为了使Spitter符合Spring Security规范,需添加两个字段role(用户角色)、enabled(是否开启权限控制)。
清单1 Spitter.java
package spittr; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.hibernate.validator.constraints.Email; import spittr.data.Role; /** * 用户类 * @author lenovo * */ @Entity public class Spitter { private Long id; //非空,5到16个字符 @NotNull @Size(min=5, max=16, message="{username.size}") private String username; @NotNull @Size(min=5, max=25, message="{password.size}") private String password; @NotNull @Size(min=2, max=60, message="{firstName.size}") private String fullName; @NotNull @Email private String email; //是否开启权限 private boolean enabled; //用户角色 private String role; public Spitter() {} public Spitter(String username, String password, String fullName, String email) { this(null, username, password, fullName, email); } public Spitter(Long id, String username, String password, String fullName, String email) { this.id = id; this.username = username; this.password = password; this.fullName = fullName; this.email = email; this.enabled = true; this.role = Role.User.getRole(); } @Id @GeneratedValue(strategy = GenerationType.AUTO) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public boolean getEnabled() { return enabled; } public void setRole(String role) { this.role = role; } public String getRole() { return role; } @Override public boolean equals(Object that) { return EqualsBuilder.reflectionEquals(this, that, "fullName", "username", "password", "email"); } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this, "fullName", "username", "password", "email"); } }
清单2 Spittle.java
package spittr; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @Entity public class Spittle { private Long id; //消息内容 private String message; //发表时间 private Date postedTime; private Spitter spitter; public Spittle() { } public Spittle(Spitter spitter, String message, Date postedTime) { this.spitter = spitter; this.message = message; this.postedTime = postedTime; } public Spittle(Long id, Spitter spitter, String message, Date postedTime) { this.id = id; this.spitter = spitter; this.message = message; this.postedTime = postedTime; } @Id @GeneratedValue(strategy = GenerationType.AUTO) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public Date getPostedTime() { return postedTime; } public void setPostedTime(Date postedTime) { this.postedTime = postedTime; } @ManyToOne @JoinColumn(name="spitterId") public Spitter getSpitter() { return this.spitter; } public void setSpitter(Spitter spitter) { this.spitter = spitter; } @Override public boolean equals(Object that) { return EqualsBuilder.reflectionEquals(this, that, "id", "postedTime"); } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this, "id", "postedTime"); } }
清单3 RootConfig.java:
package spittr.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @Configuration @Import(DefaultAppConfig.class) //导入spring jdbc配置 @ComponentScan(basePackages={"spittr"}, excludeFilters={ @ComponentScan.Filter(type=FilterType.ANNOTATION, value=EnableWebMvc.class) }) public class RootConfig { }
package spittr.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity //启用Spring MVC安全性,@EnableWebMvcSecurity已过时
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
// TODO Auto-generated method stub
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username, password,
" + "true password from Spitter where username=?")
.authoritiesByUsernameQuery("select username, 'USER' from Spitter where username=?");;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.formLogin() //基于表单的登 录认证
.loginPage("/login")
.and().logout().logoutSuccessUrl("/")
.and().httpBasic() //HTTP Basic方式的认证
.and().rememberMe().tokenValiditySeconds(2419200).key("spittrkey")
.and()
.authorizeRequests()
.antMatchers("/spitter/me").authenticated()
//指定了对“/spitters/me”路径的请求需要进行认证
.antMatchers(HttpMethod.POST, "/spittles").authenticated()
//对“/spittles”路径的HTTP POST请 求必须要经过认证
.anyRequest().permitAll(); //其他所 有的请求都是允许的, 不需认证
}
}
清单5 Role.java:
package spittr.data; public enum Role { User("USER"), Admin("ADMIN"); private String role; private Role(String role) { this.role = role; } // get set 方法 public String getRole() { return role; } public void setRole(String role) { this.role = role; } }
package spittr.data; import java.util.List; import spittr.Spitter; public interface SpitterRepository { void save(Spitter spitter); Spitter findByUsername(String username); Spitter findOne(long id); List<Spitter> findAll(); long count(); }
清单7 SpittleRepository.java:
package spittr.data; import java.util.List; import spittr.Spittle; public interface SpittleRepository { /** * * @param count 返回多少个Spittle对象 * @return 获取最新的Spittle列表 */ List<Spittle> findSpittles(int count); Spittle findOne(long id); void save(Spittle spittle); List<Spittle> findBySpitterId(long spitterId); void delete(long id); long count(); }
package spittr.web; import java.io.File; import java.io.IOException; import javax.servlet.http.HttpSession; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import spittr.Spitter; import spittr.data.SpitterRepository; import spittr.form.SpitterForm; @Controller @RequestMapping("/spitter") public class SpitterController { private SpitterRepository spitterRepository; @Autowired public SpitterController(SpitterRepository spitterRepository) { this.spitterRepository = spitterRepository; } //处理"/spitter/register"的GET请求 @RequestMapping(value="/register", method=RequestMethod.GET) public String showRegistrationForm(Model model) { model.addAttribute(new SpitterForm()); return "registerForm"; } // @RequestMapping(value="/register", method=RequestMethod.POST) // public String processRegistration( // @RequestPart("profilePicture") byte[] profilePicture, // @Valid Spitter spitter, // Errors errors) { // //如果校验出现错误,则重新返回表单 // if (errors.hasErrors()) { // System.out.println("注册有误,返回到注册页面"); // return "registerForm"; // } // spitterRepository.save(spitter); // return "redirect:/spitter/" + spitter.getUsername(); // } @RequestMapping(value="/register", method=RequestMethod.POST) public String processRegistration( RedirectAttributes redirectAttributes, @Valid SpitterForm spitterForm, HttpSession httpSession, Errors errors) throws IllegalStateException, IOException { if (errors.hasErrors()) { return "registerForm"; } Spitter spitter = spitterForm.toSpitter(); System.out.println("保存用户信息"); spitterRepository.save(spitter); MultipartFile profilePicture = spitterForm.getProfilePicture(); System.out.println("保存图片"); System.out.println("上传图片名字:" + profilePicture.getOriginalFilename()); profilePicture.transferTo(new File(spitter.getUsername() + ".jpg")); redirectAttributes.addAttribute("username", spitter.getUsername()); redirectAttributes.addFlashAttribute("spitter", spitter); httpSession.setAttribute("spitter", spitter); return "redirect:/spitter/{username}"; } @RequestMapping(value="/{username}", method=RequestMethod.GET) public String showSpitterProfile(@PathVariable String username, Model model) { // Spitter spitter = spitterRepository.findByUsername(username); // model.addAttribute(spitter); // return "profile"; //如果模型中不包含spitter属性的话,那 么showSpitterProfile()将会从Repository中查找Spitter,并 将其存放到模型中。 if(!model.containsAttribute("spitter")) { System.out.println("模型中不包含spitter属性"); model.addAttribute(spitterRepository.findByUsername(username)); } return "profile"; } @RequestMapping(value="/me", method=RequestMethod.GET) public String me() { System.out.println("ME ME ME ME ME ME ME ME ME ME ME"); return "home"; } }
package spittr.web; import java.util.Date; import java.util.List; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import spittr.Spitter; import spittr.Spittle; import spittr.data.SpitterRepository; import spittr.data.SpittleRepository; import spittr.form.SpittleForm; @Controller @RequestMapping("/spittles") public class SpittleController { private static final String MAX_LONG_AS_STRING = "9223372036854775807"; private SpittleRepository spittleRepository; private SpitterRepository spitterRepository; @Autowired public SpittleController(SpittleRepository spittleRepository, SpitterRepository spitterRepository) { this.spittleRepository = spittleRepository; this.spitterRepository = spitterRepository; } /* @RequestMapping(method=RequestMethod.GET) public String spittles(Model model){ //将spittle添加到模型中 model.addAttribute("spittleList", spittleRepository.findSpittles(Long.MAX_VALUE, 20)); return "spittles"; } */ //通过 @RequestParam 注解指定所绑定的 URL 参数 @RequestMapping(method=RequestMethod.GET) public List<Spittle> spittles( //@RequestParam(value="max", defaultValue=MAX_LONG_AS_STRING) long max, @RequestParam(value="count", defaultValue="20") int count) { //return spittleRepository.findSpittles(max, count); return spittleRepository.findSpittles(count); } @RequestMapping(value="/show", method=RequestMethod.GET) public String showSpittle( @RequestParam("spittle_id") long spittleId, Model model) { System.out.println("处理show请求"); model.addAttribute(spittleRepository.findOne(spittleId)); return "spittle"; } @RequestMapping(value="/{spittleId}", method=RequestMethod.GET) public String spittle( @PathVariable long spittleId, Model model) { Spittle spittle = spittleRepository.findOne(spittleId); if (spittle == null) { System.out.println("抛出异常"); throw new SpittleNotFoundException(); } model.addAttribute(spittle); return "spittle"; } // @RequestMapping(method=RequestMethod.POST) // public String saveSpittle(SpittleForm form, Model model) { // try { // spittleRepository.save(new Spittle(null, form.getMessage(), new Date(), // form.getLongitude(), form.getLatitude())); // return "redirect:/spittles"; // } catch (DuplicateSpittleException e) { // System.out.println("重复异常"); // return "error/duplicate"; // } // } @RequestMapping(method=RequestMethod.POST) public String saveSpittle(SpittleForm form, Model model, HttpSession httpSession) { //spittleRepository.save(new Spittle(null, form.getSpitter(), form.getMessage(), new Date())); Spitter spitter = null; if(httpSession.getAttribute("spitter") != null) { System.out.println("在Spittle页面模型中包含spitter属性"); spitter = (Spitter) httpSession.getAttribute("spitter"); } else { UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext() .getAuthentication() .getPrincipal(); String username = userDetails.getUsername(); System.out.println("页面获取Spring Security登录用户: " + username); spitter = spitterRepository.findByUsername(username); } spittleRepository.save(new Spittle(spitter, form.getMessage(), new Date())); return "redirect:/spittles"; } @ExceptionHandler(DuplicateSpittleException.class) public String handleNotFound() { return "error/duplicate"; } }
11.1.2 构建不依赖于Spring的Hibernate代码
现在的最佳实践是不再使用HibernateTemplate,而是使用上下文Session(Contextual session)。通过这种方式,会直接将Hibernate SessionFactory装配到Repository中,并使用它来获取Session,如下面的程序清单所示。
清单 HibernateSpittlerRepository.java:
package spittr.data; import java.util.List; import javax.inject.Inject; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import spittr.Spitter; //代表此为一个dao层实现 @Repository @Transactional public class HibernateSpitterRepository implements SpitterRepository { private SessionFactory sessionFactory; @Inject public HibernateSpitterRepository(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; //<co id="co_InjectSessionFactory"/> } private Session currentSession() { return sessionFactory.getCurrentSession();//<co id="co_RetrieveCurrentSession"/> } @Override public void save(Spitter spitter) { // TODO Auto-generated method stub currentSession().save(spitter); } @Override public Spitter findByUsername(String username) { // TODO Auto-generated method stub return (Spitter) currentSession().createCriteria(Spitter.class) .add(Restrictions.eq("username", username)).list().get(0); } @Override public Spitter findOne(long id) { // TODO Auto-generated method stub return (Spitter) currentSession().get(Spitter.class, id); } @SuppressWarnings("unchecked") @Override public List<Spitter> findAll() { // TODO Auto-generated method stub return (List<Spitter>) currentSession() .createCriteria(Spitter.class).list(); } @Override public long count() { // TODO Auto-generated method stub return findAll().size(); } }
package spittr.data; import java.util.List; import javax.inject.Inject; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import spittr.Spittle; //代表此为一个dao层实现 @Repository @Transactional public class HibernateSpittleRepository implements SpittleRepository { private SessionFactory sessionFactory; @Inject public HibernateSpittleRepository(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } private Session currentSession() { return sessionFactory.getCurrentSession();//<co id="co_RetrieveCurrentSession"/> } @SuppressWarnings("unchecked") @Override public List<Spittle> findSpittles(int count) { // TODO Auto-generated method stub return (List<Spittle>) spittleCriteria().setMaxResults(count).list(); } @Override public Spittle findOne(long id) { // TODO Auto-generated method stub return (Spittle) currentSession().get(Spittle.class, id); } @Override public void save(Spittle spittle) { // TODO Auto-generated method stub // Serializable id = currentSession().save(spittle); // return new Spittle( // (Long) id, // spittle.getSpitter(), // spittle.getMessage(), // spittle.getPostedTime()); currentSession().save(spittle); } @SuppressWarnings("unchecked") @Override public List<Spittle> findBySpitterId(long spitterId) { // TODO Auto-generated method stub return spittleCriteria() .add(Restrictions.eq("spitter.id", spitterId)) .list(); } @Override public void delete(long id) { // TODO Auto-generated method stub currentSession().delete(findOne(id)); } @Override public long count() { // TODO Auto-generated method stub return spittleCriteria().list().size(); } private Criteria spittleCriteria() { return currentSession() .createCriteria(Spittle.class) //.add(Restrictions.le("id", max)) .addOrder(Order.desc("postedTime")); } }
如果抛出异常:
org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister
请检查类的setter/getter方法拼写是否符合规范
如果抛出异常:
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
需添加@Transactional
执行结果:
(1) 访问主页:
![SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客](http://img1.ph.126.net/kUCjGrpN0doaixXv4KbrWw==/106116066320198905.png)
(2)访问注册页面,并输入数据,提交
![SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客](http://img0.ph.126.net/s3nLUOea1UiiD8ClSvuuCQ==/6608207819447951364.png)
(3)注册成功
![SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客](http://img2.ph.126.net/iQNYPaYY8ev0qu4PadNUdA==/2121476899568552438.png)
![SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客](http://img2.ph.126.net/e11_pzDOHJuKL56n382fQg==/6597676697077288541.png)
(5)输入文本,点击添加,进入权限验证页面:
![SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客](http://img1.ph.126.net/FcNTQvpMLwylvKaa7UmVYQ==/804455483539512805.png)
![SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客](http://img2.ph.126.net/EYCGhaFiaIgFJ-N9VaR6oA==/1803973125839029490.png)