SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)

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表。
       程序流程为:
SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客

为了使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) 访问主页:
SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客

(2)访问注册页面,并输入数据,提交

SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客
(3)注册成功

SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客

  (4)点击Spittles链接
SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客

(5)输入文本,点击添加,进入权限验证页面:

SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客

 

 用新注册的号码验证成功后,重新进入Spittles发文:

SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(上)_Hibernate - 叶主任 - 叶主任的博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值