您的位置:首页 > 编程语言 > Java开发

Spring v3.0.2 Learning Note 12 - Integrate with Hiberate

2010-10-25 11:15 316 查看
与hibernate的集成

环境

Spring v3.0.2

Hibernate V3.3.1

MySql v5.0.22

JDK 1.6.x

数据库脚本:

drop database if exists `springlearn`;

create database `springlearn`;

use `springlearn`;

create table `springlearn`.`spring_seq_no` (

seq_no int(20) not null primary key,

table_code varchar(20) not null

)

ENGINE=INNODB

DEFAULT CHARSET=utf8;

create table `springlearn`.`spring_product` (

product_id int(20) not null primary key,

product_name varchar(20) not null

)

ENGINE=INNODB

DEFAULT CHARSET=utf8;

参照http://wangxiangblog.blogspot.com/2010/04/hibernate-tools-with-eclipse.html 自动生成域对象

// Generated Oct 10, 2010 9:45:03 AM by Hibernate Tools 3.2.4.GA

package com.spring.jdbc.dao.domain;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.Table;

/**

* SpringSeqNo generated by hbm2java

*/

@Entity

@Table(name = "spring_seq_no", catalog = "springlearn")

public class SpringSeqNo implements java.io.Serializable {

private static final long serialVersionUID = -198923549321643996L;

private Long seqNo;

private String tableCode;

public SpringSeqNo() {

}

public SpringSeqNo(Long seqNo, String tableCode) {

this.seqNo = seqNo;

this.tableCode = tableCode;

}

@Id

@Column(name = "seq_no", unique = true, nullable = false)

public Long getSeqNo() {

return this.seqNo;

}

public void setSeqNo(Long seqNo) {

this.seqNo = seqNo;

}

@Column(name = "table_code", nullable = false, length = 20)

public String getTableCode() {

return this.tableCode;

}

public void setTableCode(String tableCode) {

this.tableCode = tableCode;

}

public String toString(){

return "SpringSeqNo{seqNo="+seqNo+",tableCode="+tableCode+"}";

}

}

--------------------------------------------------------------------------------------------

// Generated Oct 10, 2010 9:45:03 AM by Hibernate Tools 3.2.4.GA

package com.spring.jdbc.dao.domain;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.Table;

/**

* SpringProduct generated by hbm2java

*/

@Entity

@Table(name = "spring_product", catalog = "springlearn")

public class SpringProduct implements java.io.Serializable {

private static final long serialVersionUID = -4565317465342411183L;

private Long productId;

private String productName;

public SpringProduct() {

}

public SpringProduct(Long productId, String productName) {

this.productId = productId;

this.productName = productName;

}

@Id

@Column(name = "product_id", unique = true, nullable = false)

public Long getProductId() {

return this.productId;

}

public void setProductId(Long productId) {

this.productId = productId;

}

@Column(name = "product_name", nullable = false, length = 20)

public String getProductName() {

return this.productName;

}

public void setProductName(String productName) {

this.productName = productName;

}

public String toString() {

return "SpringProduct{productId=" + productId + ",productName="

+ productName + "}";

}

}

配置文件

Spring配置文件内容为:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.spring.jdbc" />

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"

destroy-method="close">

<property name="driverClassName" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://localhost:3306/springlearn" />

<property name="username" value="root" />

<property name="password" value="" />

<!-- 连接池启动时的初始值 -->

<property name="initialSize" value="5" />

<!-- 连接池的最大值 -->

<property name="maxActive" value="500" />

<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->

<property name="maxIdle" value="10" />

<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->

<property name="minIdle" value="5" />

</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

<property name="dataSource" ref="dataSource" />

<property name="annotatedClasses">

<list>

<value>

com.spring.jdbc.dao.domain.SpringProduct

</value>

<value>

com.spring.jdbc.dao.domain.SpringSeqNo

</value>

</list>

</property>

<property name="hibernateProperties">

<props>

<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>

<prop key="hibernate.show_sql">false</prop>

<prop key="hibernate.current_session_context">thread</prop>

</props>

</property>

</bean>

<bean id="txManager"

class="org.springframework.orm.hibernate3.HibernateTransactionManager
">

<property name="sessionFactory" ref="sessionFactory" />

</bean>

<tx:annotation-driven transaction-manager="txManager" />

</beans>

无需传统的hibernate.cfg.xml

源代码

package com.spring.jdbc.dao;

import com.spring.jdbc.dao.domain.SpringProduct;

public interface IProductDao {

public void saveProduct(SpringProduct product) ;

public Long getSeqNo(String tableCode, int increment);

}

--------------------------------------------------------------------------------------

package com.spring.jdbc.dao.impl;

import javax.annotation.Resource;

import org.hibernate.Query;

import org.hibernate.SessionFactory;

import org.springframework.stereotype.Repository;

import org.springframework.transaction.annotation.Transactional;

import com.spring.jdbc.dao.IProductDao;

import com.spring.jdbc.dao.domain.SpringProduct;

import com.spring.jdbc.dao.domain.SpringSeqNo;

@Repository

@Transactional

public class ProductDao implements IProductDao {

@Resource

private SessionFactory sessionFactory;

public void setSessionFactory(SessionFactory sessionFactory) {

this.sessionFactory = sessionFactory;

}

private static final int FIRST_SEQ_NUM = 1;// init No

private static final String SQL_GET_SEQ_NUM = "select t from SpringSeqNo as t where t.tableCode=:table_code";

private static final String SQL_UPDATE_SEQ_NUM = "update SpringSeqNo
set seqNo=seqNo+:increment where tableCode=:table_code";

@Override

@Transactional

public void saveProduct(SpringProduct product) {

Long currSeqNo = getSeqNo("PRODUCT", 1);//取下一个ID,该值由表spring_seq_no存储

product.setProductId(currSeqNo);

sessionFactory.getCurrentSession().save(product);

System.out.println("save " + product);

}

@Override

@Transactional

public synchronized Long getSeqNo(String tableCode, int increment) {

SpringSeqNo result = null;

int row = 0;

// Important, update the table first to lock the record.

Query queryUpdate = sessionFactory.getCurrentSession().createQuery(

SQL_UPDATE_SEQ_NUM); //先update数据库加上锁,而不是先查!!


queryUpdate.setString("table_code", tableCode);

queryUpdate.setInteger("increment", increment);

row = queryUpdate.executeUpdate();

if (row == 0) {

// row=0 means it is the first time to get seq no.

// insert a new record which seq_no=1

SpringSeqNo seqNo = new SpringSeqNo();

seqNo.setTableCode(tableCode);

seqNo.setSeqNo(Long.valueOf(increment + FIRST_SEQ_NUM));

sessionFactory.getCurrentSession().save(seqNo);

result = new SpringSeqNo();

result.setTableCode(tableCode);

result.setSeqNo(Long.valueOf(FIRST_SEQ_NUM));

} else {

/** get current sequence no. */

Query query = sessionFactory.getCurrentSession().createQuery(

SQL_GET_SEQ_NUM);

query.setString("table_code", tableCode);

SpringSeqNo temp = (SpringSeqNo) query.setMaxResults(1)

.uniqueResult();

result = new SpringSeqNo();

result.setTableCode(tableCode);

result.setSeqNo(temp.getSeqNo() - increment);

}

return result.getSeqNo();

}

}

----------------------------------------------------------------------------------------------------

写一个多线程测试看是否可正确取到sequence number

package com.spring.jdbc.test;

import org.springframework.context.support.AbstractApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.jdbc.dao.IProductDao;

import com.spring.jdbc.dao.domain.SpringProduct;

public class ProductProcessor extends Thread {

private int number = 100;

AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(

"spring.xml");

public ProductProcessor() {

}

public void run() {

while (number > 0) {

IProductDao prodDao = (IProductDao) ctx.getBean("productDao");

String threadName = Thread.currentThread().getName();

SpringProduct product = new SpringProduct();

product.setProductName(threadName);

prodDao.saveProduct(product);

number--;

}

}

}

-----------------------------------------

package com.spring.jdbc.test;

public class Tester {

public static void main(String[] args) {

// TODO Auto-generated method stub

Thread t1 = new ProductProcessor();

Thread t2 = new ProductProcessor();

Thread t3 = new ProductProcessor();

Thread t4 = new ProductProcessor();

Thread t5 = new ProductProcessor();

t1.start();

t2.start();

t3.start();

t4.start();

t5.start();

}

}

注意:

先在spring_seq_no给一个初始值,

insert into spring_seq_no(seq_no,table_code) value (1,'PRODUCT');

运行Tester.java,可以看到每个线程可以拿到正确的ID.

如果不给初始值, 会碰到这个异常。

Caused by: java.sql.BatchUpdateException: Deadlock found when trying to get lock; try restarting transaction

at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1684)

at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1108)

at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297)

at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)

at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)

在ProductDao.java中的saveProduct()和getSeqNo()中任意位置抛出RuntimeException,事务会回滚;
两个方法中的 sessionFactory.getCurrentSession()
对应的session是同一个session,事务也是同一个事务。

以上代码也在Oralce 10g环境下测试,可得同样的结果。

即便不用spring来管理事务,而是通过hibernate单独处理,也可得同样的结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: