——跟我一起学 Hibernate 系列(2)
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.7maven.compiler.source>
<maven.compiler.target>1.7maven.compiler.target>
<hibernate.jpa21.api.version>1.0.0.Finalhibernate.jpa21.api.version>
<hibernate.version>5.1.0.Finalhibernate.version>
<validation.api.version>1.1.0.Finalvalidation.api.version>
<hibernate.validator.version>5.2.1.Finalhibernate.validator.version>
<javax-el.version>3.0.1-b04javax-el.version>
<slf4j.impl.version>1.6.1slf4j.impl.version>
<testing.version>6.8.7testing.version>
<btm.version>2.1.3btm.version>
properties>
<dependencies>
<dependency>
<groupId>org.testnggroupId>
<artifactId>testngartifactId>
<version>${testing.version}version>
<exclusions>
<exclusion>
<groupId>junitgroupId>
<artifactId>junitartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-jdk14artifactId>
<version>${slf4j.impl.version}version>
dependency>
<dependency>
<groupId>org.codehaus.btmgroupId>
<artifactId>btmartifactId>
<version>${btm.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-entitymanagerartifactId>
<version>${hibernate.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>${hibernate.validator.version}version>
dependency>
<dependency>
<groupId>javax.elgroupId>
<artifactId>javax.el-apiartifactId>
<version>${javax-el.version}version>
dependency>
<dependency>
<groupId>org.glassfishgroupId>
<artifactId>javax.elartifactId>
<version>${javax-el.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-enversartifactId>
<version>${hibernate.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-ehcacheartifactId>
<version>${hibernate.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.34version>
dependency>
dependencies>
package net.deniro.hibernate.env;
import bitronix.tm.resource.jdbc.PoolingDataSource;
import java.util.Properties;
/**
*
* 数据库产品(目前只支持 MYSQL)
*
* @author Deniro Li
* 2017/1/13
*/
public enum DatabaseProduct {
MYSQL(
new DataSourceConfiguration() {
@Override
public void configure(PoolingDataSource ds, String connectionURL) {
ds.setClassName("bitronix.tm.resource.jdbc.lrc.LrcXADataSource");
ds.getDriverProperties().put(
"url",
connectionURL != null ? connectionURL
: "jdbc:mysql://localhost:3306/hibernate?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"
);
Properties dp=ds.getDriverProperties();
dp.put("driverClassName", "com.mysql.jdbc.Driver");
dp.put("user","root");
dp.put("password","");
ds.setDriverProperties(dp);
}
},
//MySQL57InnoDBDialect 可用于 MySQL5.6
org.hibernate.dialect.MySQL57InnoDBDialect.class.getName()
);
public DataSourceConfiguration configuration;
public String hibernateDialect;
private DatabaseProduct(DataSourceConfiguration configuration, String hibernateDialect) {
this.configuration = configuration;
this.hibernateDialect = hibernateDialect;
}
public interface DataSourceConfiguration {
void configure(PoolingDataSource ds, String connectionURL);
}
}
package net.deniro.hibernate.env;
import bitronix.tm.TransactionManagerServices;
import bitronix.tm.resource.jdbc.PoolingDataSource;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import java.util.logging.Logger;
/**
* 使用 Bitronix 作为数据库事务
*
* @author Deniro Li
* 2017/1/13
*/
public class TransactionManagerSetup {
public static final String DATASOURCE_NAME = "deniroDS";
private static final Logger logger = Logger.getLogger(TransactionManagerSetup.class
.getName());
protected final Context context = new InitialContext();
protected final PoolingDataSource dataSource;
public final DatabaseProduct databaseProduct;
public TransactionManagerSetup(DatabaseProduct databaseProduct) throws Exception {
this(databaseProduct, null);
}
public TransactionManagerSetup(DatabaseProduct databaseProduct, String connectionURL)
throws Exception {
logger.fine("启动数据库连接池");
logger.fine("为事务设置一个稳定的唯一标识");
TransactionManagerServices.getConfiguration().setServerId("deniroServer1");
logger.fine("关闭 JMX(为了单元测试方便)");
TransactionManagerServices.getConfiguration().setDisableJmx(true);
logger.fine("关闭事务日志(为了单元测试方便)");
TransactionManagerServices.getConfiguration().setJournal(null);
logger.fine("关闭在一个事务中无法获取数据库连接的警告信息");
TransactionManagerServices.getConfiguration().setWarnAboutZeroResourceTransaction
(false);
logger.fine("创建数据库连接池");
dataSource = new PoolingDataSource();
dataSource.setUniqueName(DATASOURCE_NAME);
dataSource.setMinPoolSize(1);
dataSource.setMaxPoolSize(5);
dataSource.setPreparedStatementCacheSize(10);
// 这里明确指定事务隔离级别,为了后面的高级特性展示
dataSource.setIsolationLevel("READ_COMMITTED");
//当 EntityManager 被挂起或者没有被加入事务的情况下,允许事务自动提交
dataSource.setAllowLocalTransactions(true);
logger.info("选定的数据库是:" + databaseProduct);
this.databaseProduct = databaseProduct;
databaseProduct.configuration.configure(dataSource, connectionURL);
logger.fine("初始化事务与资源管理器");
dataSource.init();
}
public Context getNamingContext() {
return context;
}
public UserTransaction getUserTransaction() {
try {
return (UserTransaction) getNamingContext().lookup("java:comp/UserTransaction");
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public DataSource getDataSource() {
try {
return (DataSource) getNamingContext().lookup(DATASOURCE_NAME);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void rollback() {
UserTransaction tx = getUserTransaction();
try {
if (tx.getStatus() == Status.STATUS_ACTIVE || tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
tx.rollback();
} catch (SystemException e) {
System.err.print("事务回滚失败!");
e.printStackTrace(System.err);
}
}
public void stop() throws Exception {
logger.fine("关闭数据库连接池");
dataSource.close();
TransactionManagerServices.getTransactionManager().shutdown();
}
}
# Bitronix 内建了一个 JNDI contgext,所以这里直接绑定对应的类就好
java.naming.factory.initial=bitronix.tm.jndi.BitronixInitialContextFactory
package net.deniro.hibernate.env;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import java.util.Locale;
/**
* 在一个单元测试中,开启或关闭事务管理器或者数据库连接池
*
*
* @author Deniro Li
* 2017/1/13
*/
public class TransactionManagerTest {
//Static single database connection manager per test suite
static public TransactionManagerSetup TM;
@Parameters({"database", "connectionURL"})
@BeforeSuite
public void beforeSuite(@Optional String database, @Optional String connectionURL)
throws Exception {
TM = new TransactionManagerSetup(database != null ? DatabaseProduct.valueOf(database
.toUpperCase(Locale.CHINESE)) : DatabaseProduct.MYSQL, connectionURL);
}
@AfterSuite(alwaysRun = true)
public void afterSuite() throws Exception {
if (TM != null)
TM.stop();
}
}
package net.deniro.hibernate.model.helloworld;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Deniro Li
* 2017/1/13
*/
@Entity
public class Message {
@Id
@GeneratedValue//自动生成 ID
private Long id;
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
<persistence
version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence_2_1.xsd">
<persistence-unit name="HelloWorldPU">
<jta-data-source>deniroDSjta-data-source>
<class>net.deniro.hibernate.model.helloworld.Messageclass>
<exclude-unlisted-classes>trueexclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action"
value="drop-and-create"/>
<property name="hiberate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
properties>
persistence-unit>
persistence>
package net.deniro.hibernate.example.helloworld;
import net.deniro.hibernate.env.TransactionManagerTest;
import net.deniro.hibernate.model.helloworld.Message;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.transaction.UserTransaction;
import java.util.List;
import static org.testng.AssertJUnit.assertEquals;
/**
* 基于 JPA 的 HelloWorld
*
* @author Deniro Li
* 2017/1/13
*/
public class HelloWorldJPA extends TransactionManagerTest {
@Test
public void storeLoadMessage() throws Exception {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("HelloWorldPU");
try {
{
//保存
UserTransaction tx = TM.getUserTransaction();
tx.begin();
EntityManager em = emf.createEntityManager();
Message message = new Message();
message.setText("Hello World!");
em.persist(message);
tx.commit();
em.close();
}
{
//查询
UserTransaction tx = TM.getUserTransaction();
tx.begin();
EntityManager em = emf.createEntityManager();
List messages = em.createQuery("select m from Message m")
.getResultList();
assertEquals(messages.size(), 1);
assertEquals(messages.get(0).getText(), "Hello World!");
//更新
messages.get(0).setText("Take me to your leader!");
tx.commit();
em.close();
}
} finally {
TM.rollback();
emf.close();
}
}
}
运行测试用例:
自此,我们基于 JPA 的 Hibernate 环境就搭建好啦 O(∩_∩)O~