Spring与Hibernate整合实现(SH框…

来源:互联网 发布:小额贷款骗局知乎 编辑:程序博客网 时间:2024/06/09 17:43
  先放图,本节的工程文件目录结构如下:
Spring与Hibernate整合实现(SH框架)

以及要导入的额外库文件(记得加上Hibernate库和SpringFramework的库):
Spring与Hibernate整合实现(SH框架)
其中:
aopalliance.jar
aspectjweaver-1.7.2.jar
可能需要到网上下载。随便百度个就行,蛮多人分享的。

  Spring与Hibernate两边结合在一起之后,Spring对Hibernate的一些启动操作进行了接管。毕竟Spring本身是一个管理类工厂的服务,而许多程序、应用的初始化操作,可以交给Spring来完成。
  于是,Hibernate里面的数据库地址、用户名、密码和操作参数等,统统交给Spring来接管了,我们无需担心。于是Hibernate.cfg.xml文件变得异常简洁。记得在上一节讲解Hibernate的时候,启动一个服务要写相当多的启动代码

      Configuration cfg = newConfiguration().configure();
       //如果是hibernate4.0以前的版本,使用如下的方式创建SessionFactory对象
       // SessionFactory factory =cfg.buildSessionFactory();
      StandardServiceRegistryBuilder ssrb = newStandardServiceRegistryBuilder()
            .applySettings(cfg.getProperties());
      ServiceRegistry service = ssrb.build();
      SessionFactory factory =cfg.buildSessionFactory(service);
      
       Sessionsession = factory.openSession();
      Transaction tx = session.beginTransaction();

然后才是业务代码(也就是功能代码):

      try {
          Student stu = (Student)session.get(Student.class, 1);
         stu.setName("lisi");
          tx.commit();
       } catch(Exception e) { 
          tx.rollback();
       } finally{
          session.close();
      }

  其实Hibernate只需要启动一次,然后整个项目都用一个或者两个实例就好根据线程的负载和锁的情况,别造成这个用了Hibernate,那个急着用又用不上又不能自己生成一个实例来用。所以具体怎么管理,怎么生成,或者什么时候用这些类的实例Configuration、StandardServiceRegistryBuilder、ServiceRegistry、SessionFactory等等就交给Spring框架来决定吧。我们看到,其实最主要的功能,集中在session处。如何保存、如何从数据库获取一行,都要靠session来执行。那么其实把冗余的初始化功能分离后,剩下的东西,写进一个功能模块里面,就成了DAO(DataAccess Object数据访问对象)。我们来看这么一段代码:
文件BookDao.java
public interface BookDao{
   public String findBookById(int id);
   public void saveBook(Book book);
}
  可以看到,其实DAO类(或接口)就是一个封装了数据库操作的类(或接口)。DAO意味着封装了非常非常具体的数据库操作后,直接得到的服务。它可以是查询某个id得到这个id下的书名字符串,也可以是新建并保存一个书本条目。我们看到这个类使用了POJO类Book,Book的定义会在后面给出。具体怎么塞到数据库里面的,得由这个类的实现(implements)来进行:
文件BookDaoImpl.java
package dao_service;

importorg.hibernate.Query;
importorg.hibernate.Session;
importorg.hibernate.SessionFactory;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Repository;
importtable.book.Book;

@Repository
public class BookDaoImplimplements BookDao{
   @Autowired
   private SessionFactorysessionFactory;
   //获取和当前线程绑定的Session
   private Session getSession(){
       returnsessionFactory.getCurrentSession();
   }
   @Override public StringfindBookById(int id){
      
       //String hql="SELECT bookNamefrom Book where id=?";//直接使用id=?会有问题
       //Queryquery=getSession().createQuery(hql).setInteger(0, id);
       Stringhql="SELECT bookName from Book where id=?0";//使用JPA方式,改为?0
                                                                                   //即可用setParameter设置参数
       Queryquery=getSession().createQuery(hql);
      query.setParameter("0", id);
      
       Stringstr= query.uniqueResult().toString();
       returnstr;
   }
   @Override public voidsaveBook(Book book){
      getSession().save(book);
   }
}
  从这个BookDaoImpl类实现,我们可以看到内置了一个SessionFactory。细心的同学会发现,有这个private类,却没有具体的实现,也没有setSessionFactory方法来设置它。它在saveBook()方法使用的时候不会是个空指针吗?
  奥妙就在这里,其实这个类内实例的导入,是由Spring来完成的。Spring跟Hibernate结合以后,提供了一系列自动化方法,比如可以自动搜索dao_service文件夹(也就是BookDao.java和BookDaoImpl.java存放的文件夹)下的类,根据关键词过滤来判断哪些是类的实现,根据方法名或者内部私有变量的名字,来判断装入何种类的实现(如具体的SessionFactory等等)。
  为了验证自动装配这一点,我们给它再套一个壳子,我们新建一个BookService类,里面提供的服务和内容跟BookDao类完全一样:
文件BookService.java
public interfaceBookService{
   public String findBookById(int id);
   public void saveBook(Book book);
}
然后我们建立一个实现,同样是在接口名后面加上Impl:
文件BookServiceImpl.java
package dao_service;

importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Service;
importtable.book.Book;

@Service
public class BookServiceImplimplements BookService
{
   @Autowired
   private BookDao bookDao;
   @Override
   public String findBookById(int id)
   {
       returnbookDao.findBookById(id);
   }
   @Override
   public void saveBook(Book book)
   {
      bookDao.saveBook(book);
      
   }
}
  这个壳子啥都没做,仅仅是用了BookDao接口的方法来完成自己的方法。注意,在BookServiceImpl里所使用的不是BookDaoImpl的方法,而是BookDao,说明我们要想使用,必须得有个具体的实现。意味着整个过程必须有人来搞。这个人显然就是Spring。有兴趣的同学可以写一个计数器,看看Spring生成了多少个实例,并且都调用了哪个实例来使用(提示:用静态变量作计数器,则所有实例之间都可以共享了。不懂是否线程安全,为了准确计数,可以考虑加锁
  说了这么多,那么实体类(EntityClass)呢?在这里定义:
文件Book.java
package table.book;

//importorg.hibernate.annotations.Entity;这个已经过时,用javax里面的entity代替
importjavax.persistence.Entity;
importjavax.persistence.Id;
importjava.io.Serializable;
@Entity
public class Book implementsSerializable {
   private static final long serialVersionUID =1L;
   @Id
   private Integer id;
   private String bookName;
   private String isbn;
   private int price;
   private int stock;

   public Book() {
   }
   public Book(Integer id, String bookName, Stringisbn, int price, int stock){
      super();
       this.id =id;
      this.bookName = bookName;
       this.isbn= isbn;
       this.price= price;
       this.stock= stock;
   }
   public Integer getId(){   return id;   }
   public void setId(Integer id){   this.id = id;   }
   public String getBookName(){   return bookName;   }
   public void setBookName(String bookName){   this.bookName =bookName;}
   public String getIsbn(){   return isbn;   }
   public void setIsbn(String isbn){   this.isbn = isbn;   }
   public int getPrice(){   return price;   }
   public void setPrice(int price){   this.price = price;   }
   public int getStock(){   return stock;   }
   public void setStock(int stock){   this.stock = stock;   }
}
  可以看到这个Book实体类里面有5个属性,对应着数据库表中5个列:id、bookname、isbn、price、stock。同理,给这个Book.java配置一个文件给hibernate读取:
文件Book.hbm.xml
<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mapping PUBLIC "-//Hibernate/HibernateMapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-3-15 16:30:05 byHibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
   <classname="table.book.Book"table="BOOK">
      <id name="id"type="java.lang.Integer">
          <columnname="ID"/>
          <generatorclass="native"/>
      </id>
      <propertyname="bookName"type="java.lang.String">
          <columnname="BOOK_NAME"/>
      </property>
      <propertyname="isbn"type="java.lang.String">
          <columnname="ISBN"/>
      </property>
      <propertyname="price"type="int">
          <columnname="PRICE"/>
      </property>
      <propertyname="stock"type="int">
          <columnname="STOCK"/>
      </property>
  </class>
</hibernate-mapping>

接下来我们看看具体hibernate的配置文件:
文件hibernate.cfg.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEhibernate-configuration PUBLIC
      "-//Hibernate/Hibernate Configuration DTD3.0//EN"
      "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!--
    十二月 08, 201612:48:02 下午 org.hibernate.internal.util.xml.DTDEntityResolverresolveEntity
    WARN: HHH000223:Recognized obsolete hibernate namespacehttp://hibernate.sourceforge.net/. 
    Use namespacehttp://www.hibernate.org/dtd/ instead. Refer to Hibernate 3.6Migration Guide!
   如果对这个警告心烦的,可以把网址按照警告的要求来改。
-->
<hibernate-configuration>
  <session-factory>
       <!-- 配置Hibernate的基本属性-->
       <!-- 1.数据源配置到IOC容器中-->
       <!--2.关联的.hbm.xml也在IOC容器配置SessionFactory实例-->
       <!--3.配置Hibernate的基本属性:方言,SQL显示及格式化,生成数据表的策略以及二级缓存-->
      <propertyname="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
      <propertyname="hibernate.show_sql">true</property>
      <propertyname="hbm2ddl.auto">update</property>
  </session-factory>
</hibernate-configuration>

明显少了很多东西。这些本该在hibernate.cfg.xml中的配置被转移到了Spring配置文件里面了:
文件applicationContext.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.1.xsd
      http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.1.xsd
      http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
  <!--
      http://zghbwjl.blog.163.com/blog/static/120336672201092294943744/
      一看这个错误肯定是少包或者是包冲突,知道原因就找吧,根据这句: 
      java.lang.NoClassDefFoundError:org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
      可以看出应该是少了aspectjweaver.jar这个jar包,加上就可以了。
   -->
   <!--
       context:component-scanbase-package这是拿来扫描beans所在目录的
      所以要scan所定义的beans的目录。目前定义的就是DAO一些列bean
      http://blog.csdn.net/chenweihaoshuai/article/details/8036671
       目录不对会报错:
      org.springframework.beans.factory.NoSuchBeanDefinitionException: Nobean named 'xx' isdefined
   -->
   <context:component-scanbase-package="dao_service"></context:component-scan>
   <beanid="dataSource"class="org.apache.commons.dbcp2.BasicDataSource"destroy-method="close">
      <!--
         尝试导入commons-dbcp2-2.1.1.jar包。解决这个BasicDataSource的问题
          然后确实要导入相应的pool2包
         这里可能是用来接管Hibernate的数据库定义的。账户、密码都不在hibernate.hbm.xml里面定义了
         参考资料:http://blog.csdn.net/a105421548/article/details/43016953
          密码等定义如下:
      -->
      <propertyname="driverClassName"value="com.mysql.jdbc.Driver"/>
      <!--<propertyname="url"value="jdbc:mysql://localhost/test"/>-->
      <propertyname="url"value="jdbc:mysql://localhost:3306/student_manager?useSSL=true"/>
      <propertyname="username"value="root"></property>
      <propertyname="password"value="passwordOfRoot"></property>
  </bean>
   <!--
      常规的hibernate包缺失问题。项目事实的时候,最后SSH都分开,单独测试一下
       nested exception isjava.lang.NoClassDefFoundError:javax/persistence/Entity
      缺少hibernate-jpa-2.1-api-1.0.0.Final.jar
      
       Cannot load JDBC driver class'com.mysql.jdbc.Driver'
      缺少mysql-connector-java-5.1.39-bin.jar
   -->
   <beanid="sessionFactory"class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"lazy-init="false">
      <!--注入datasource,给sessionfactoryBean内setdatasource提供数据源-->
      <propertyname="dataSource"ref="dataSource"/>
      <propertyname="configLocation"value="classpath:configuration/hibernate.cfg.xml"></property>
      <!-- 
          加载实体类的映射文件位置及名称
          路径一定全都用斜杠table/book
public class MainClass{
   public static void main(String[] args){
      
      ApplicationContext context;
       context =new ClassPathXmlApplicationContext("configuration/applicationContext.xml");
      
       DataSourcedataSource = (DataSource)context.getBean(DataSource.class);
      System.out.println("dataSource.toString()=="+dataSource);

      
      BookService bookService;
      bookService = context.getBean(BookService.class);
      bookService.saveBook(new Book(1, "Android源码分析","1002", 45,10));
      bookService.saveBook(new Book(2, "iOS源码分析","1012", 43,20));
      
       StringbookName = bookService.findBookById(1);
      System.out.println(bookName);
   }
}
  这么多代码写好以后,存储一本新书就非常简单了。首先Spring通过读取configuration里的xml文件得到配置好的context,然后context再通过getBean获取实体类,并用实体类进行数据库操作。我们可以看到多次运行后数据库里面加了很多条目。
  具体运行结果,上图:
Spring与Hibernate整合实现(SH框架)

Spring与Hibernate整合实现(SH框架)







本教程参考了如下:http://www.linuxidc.com/Linux/2016-03/129331.htm
0 0