Hibernate
随着应用程序变得越来越复杂,对持久化的需求也变得更复杂。我们需要将对象的属性映射到数据库的列上,并且需要自动生成语句和查询,这样我们就能从无休止的问号字符串中解脱出来。此外,我们还需要一些更复杂的特性:
延迟加载(Lazy loading):随着我们的对象关系变得越来越复杂,有时候并不希望立即获取完整的对象间关系。举一个典型的例子,假设在查询一组PurchaseOrder对象,而每个对象中都包含一个LineItem对象集合。如果只关心PurchaseOrder的属性,那查询出LineItem的数据就毫无意义。而且这可能是开销很大的操作。延迟加载允许只在需要的时候获取数据。
- 预先抓取(Eager fetching):这与延迟加载是相对的。借助于预先抓取,我们可以使用一个查询获取完整的关联对象。如果我们需要PurchaseOrder及其关联的LineItem对象,预先抓取的功能可以在一个操作中将它们全部从数据库中取出来,节省了多次查询的成本。
- 级联(Cascading):有时,更改数据库中的表会同时修改其他表。回到我们订购单的例子中,当删除Order对象时,我们希望同时在数据库中删除关联的LineItem。
一些可用的框架提供了这样的服务,这些服务的通用名称是对象/关系映射(object-relational mapping,ORM)。
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工厂bean都是Spring FactoryBean接口的实现,它们会产生一个HibernateSessionFactory,它能够装配进任何SessionFactory类型的属性中。
至于选择哪一个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;
}
}
清单 数据源DataSourceConfig:
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表。
程序流程为:
为了使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 {
}
清单4 SecurityConfig.java:
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;
}
}
清单6 SpitterRepository.java:
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();
}
清单8 SpitterController.java:
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";
}
}
清单9 SpittleController.java:
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();
}
}
清单 HibernateSpittleRepository.java:
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) 访问主页:
(2)访问注册页面,并输入数据,提交
(3)注册成功
(4)点击Spittles链接
(5)输入文本,点击添加,进入权限验证页面:
用新注册的号码验证成功后,重新进入Spittles发文: