原文:Mkyong
休眠错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:org/Apache/commons/logging/log factory
一个常见的 Hibernate 错误,这是由于缺少依赖库——公共日志记录造成的。
Initial SessionFactory creation failed.java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:18)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:17)
Caused by: java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
at org.hibernate.cfg.Configuration.<clinit>(Configuration.java:120)
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 4 more
解决办法
你可以在这里下载图书馆-http://commons.apache.org/logging/
或者
在 Maven 的 pom.xml 中添加依赖项
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById®;if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!‘undefined’){i[c]++} else{i[c]=1;} })(window, document, ‘InContent’, ‘script’, ‘mediaType’, ‘carambola_proxy’,‘Cbola_IC’,‘localStorage’,‘set’,‘get’,‘Item’,‘cbolaDt’,‘//web.archive.org/web/20190305140402/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0’)
休眠错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:org/dom4j/document exception
一个常见的 Hibernate 错误,这是由于缺少依赖库 dom4j 造成的。
Initial SessionFactory creation failed.java.lang.NoClassDefFoundError: org/dom4j/DocumentException
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:18)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:17)
Caused by: java.lang.NoClassDefFoundError: org/dom4j/DocumentException
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
Caused by: java.lang.ClassNotFoundException: org.dom4j.DocumentException
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 3 more
解决办法
你可以在这里下载图书馆-http://sourceforge.net/projects/dom4j/files/dom4j/
或者
在 Maven 的 pom.xml 中添加依赖项
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById®;if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!‘undefined’){i[c]++} else{i[c]=1;} })(window, document, ‘InContent’, ‘script’, ‘mediaType’, ‘carambola_proxy’,‘Cbola_IC’,‘localStorage’,‘set’,‘get’,‘Item’,‘cbolaDt’,‘//web.archive.org/web/20190301211220/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0’)
Hibernate 错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:org/hibernate/annotations/common/reflection/reflection manager
这是由于缺少 Hibernate commons 注释库造成的。
Initial SessionFactory creation failed.java.lang.NoClassDefFoundError:
org/hibernate/annotations/common/reflection/ReflectionManager
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:19)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:11)
Caused by: java.lang.NoClassDefFoundError: org/hibernate/annotations/common/reflection/ReflectionManager
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
Caused by: java.lang.ClassNotFoundException: org.hibernate.annotations.common.reflection.ReflectionManager
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 3 more
解决办法
可以从 Hibernate 官网下载该库
或者
在 Maven 的 pom.xml 中添加依赖项
<dependency>
<groupId>hibernate-commons-annotations</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.0.0.GA</version>
</dependency>
页(page 的缩写)为了下载 Hibernate 公共注释库,您可能需要包含 JBoss 存储库。
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.com/maven2/</url>
</repository>
</repositories>
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById®;if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!‘undefined’){i[c]++} else{i[c]=1;} })(window, document, ‘InContent’, ‘script’, ‘mediaType’, ‘carambola_proxy’,‘Cbola_IC’,‘localStorage’,‘set’,‘get’,‘Item’,‘cbolaDt’,‘//web.archive.org/web/20190203090236/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0’)
休眠错误–Java . lang . noclasdeffounderror:javax/transaction/synchron ization
问题
这是由于缺少“jta.jar
”造成的,通常发生在 Hibernate 事务开发中。
java.lang.NoClassDefFoundError: javax/transaction/Synchronization
at org.hibernate.impl.SessionImpl.<init>(SessionImpl.java:213)
at org.hibernate.impl.SessionFactoryImpl.openSession(SessionFactoryImpl.java:473)
at org.hibernate.impl.SessionFactoryImpl.openSession(SessionFactoryImpl.java:497)
at org.hibernate.impl.SessionFactoryImpl.openSession(SessionFactoryImpl.java:505)
at com.mkyong.common.App.main(App.java:13)
Caused by: java.lang.ClassNotFoundException: javax.transaction.Synchronization
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
... 5 more
解决办法
你可以从默认的 Maven central、JBoss 或 Java.net 库下载“jta.jar
”。
1.Maven 中央存储库
<dependencies>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
2.JBoss Maven 资源库
添加 JBoss Maven 存储库
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.com/maven2/</url>
</repository>
</repositories>
并定义“jta.jar
”明细。
<dependencies>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
3.Java net Maven 知识库
添加 Java net Maven 资源库
<repositories>
<repository>
<id>Java 2</id>
<url>http://download.java.net/maven/2/</url>
</repository>
</repositories>
并定义“ jta1.0.1B.jar
”明细。
<dependencies>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.0.1B</version>
</dependency>
</dependencies>
Note
Alternately, you can include the “javaee.jar“, which can be found under J2EE SDK’s folder. The “javaee.jar” contains “javax/transaction/Synchronization
” class as well.hibernate
休眠错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:com/m change/v2/c3p 0/data sources
Hibernate 的“C3P0”连接池错误,这是由于缺少依赖库–C3 P0 造成的。
Initial SessionFactory creation failed.java.lang.NoClassDefFoundError: com/mchange/v2/c3p0/DataSources
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:19)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:11)
Caused by: java.lang.NoClassDefFoundError: com/mchange/v2/c3p0/DataSources
at org.hibernate.connection.C3P0ConnectionProvider.configure(C3P0ConnectionProvider.java:154)
at org.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:124)
at org.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:56)
at org.hibernate.cfg.SettingsFactory.createConnectionProvider(SettingsFactory.java:410)
at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:62)
at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2009)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1292)
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
Caused by: java.lang.ClassNotFoundException: com.mchange.v2.c3p0.DataSources
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 10 more
解决办法
你可以在这里下载图书馆-http://www.mchange.com/projects/c3p0/
或者
使用 Maven 的坐标
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
追踪
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById®;if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!‘undefined’){i[c]++} else{i[c]=1;} })(window, document, ‘InContent’, ‘script’, ‘mediaType’, ‘carambola_proxy’,‘Cbola_IC’,‘localStorage’,‘set’,‘get’,‘Item’,‘cbolaDt’,‘//web.archive.org/web/20190308011746/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0’)
hibernate–获取策略示例
Hibernate 几乎没有抓取策略来优化 Hibernate 生成的 select 语句,因此它可以尽可能地高效。获取策略在映射关系中声明,以定义 Hibernate 如何获取其相关的集合和实体。
抓取策略
有四种抓取策略
1.fetch-“join” =禁用延迟加载,总是加载所有的集合和实体。
2。fetch-“select”(默认)=惰性加载所有集合和实体。
3。batch-size=“N” =提取多达“N”个集合或实体,不记录。
4。fetch-“subselect” =将其集合分组到一个 subselect 语句中。
关于详细的解释,你可以查看 Hibernate 文档。
获取策略示例
这里有一个“一对多关系”的例子来演示获取策略。一只股票是属于许多股票的日常记录。
在 XML 文件中声明提取策略的示例
...
<hibernate-mapping>
<class name="com.mkyong.common.Stock" table="stock">
<set name="stockDailyRecords" cascade="all" inverse="true"
table="stock_daily_record" batch-size="10" fetch="select">
<key>
<column name="STOCK_ID" not-null="true" />
</key>
<one-to-many class="com.mkyong.common.StockDailyRecord" />
</set>
</class>
</hibernate-mapping>
在注释中声明提取策略的示例
...
@Entity
@Table(name = "stock", catalog = "mkyong")
public class Stock implements Serializable{
...
@OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
@Cascade(CascadeType.ALL)
@Fetch(FetchMode.SELECT)
@BatchSize(size = 10)
public Set<StockDailyRecord> getStockDailyRecords() {
return this.stockDailyRecords;
}
...
}
让我们探讨一下获取策略如何影响 Hibernate 生成的 SQL 语句。
1.fetch="select "或@Fetch(FetchMode。选择)
这是默认的获取策略。它支持所有相关集合的延迟加载。让我们看看这个例子…
//call select from stock
Stock stock = (Stock)session.get(Stock.class, 114);
Set sets = stock.getStockDailyRecords();
//call select from stock_daily_record
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
StockDailyRecord sdr = (StockDailyRecord) iter.next();
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
输出
Hibernate:
select ...from mkyong.stock
where stock0_.STOCK_ID=?
Hibernate:
select ...from mkyong.stock_daily_record
where stockdaily0_.STOCK_ID=?
Hibernate 生成了两条 select 语句
1.Select 语句检索股票记录-session . get(Stock . class,114)
2。选择其相关集合-sets . iterator()
2.fetch="join "或@Fetch(FetchMode。加入)
“连接”抓取策略将禁用所有相关集合的延迟加载。让我们看看这个例子…
//call select from stock and stock_daily_record
Stock stock = (Stock)session.get(Stock.class, 114);
Set sets = stock.getStockDailyRecords();
//no extra select
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
StockDailyRecord sdr = (StockDailyRecord) iter.next();
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
输出
Hibernate:
select ...
from
mkyong.stock stock0_
left outer join
mkyong.stock_daily_record stockdaily1_
on stock0_.STOCK_ID=stockdaily1_.STOCK_ID
where
stock0_.STOCK_ID=?
Hibernate 只生成一个 select 语句,它在股票初始化时检索所有相关的集合。–session . get(stock . class,114)
1.Select 语句来检索股票记录并外部联接其相关集合。
3.batch-size="10 "或@BatchSize(size = 10)
这种“批量”获取策略总是被许多 Hibernate 开发人员误解。让我们看看这里的误解概念…
Stock stock = (Stock)session.get(Stock.class, 114);
Set sets = stock.getStockDailyRecords();
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
StockDailyRecord sdr = (StockDailyRecord) iter.next();
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
您的预期结果是什么,是从集合中每次提取 10 条记录吗?参见
输出输出
Hibernate:
select ...from mkyong.stock
where stock0_.STOCK_ID=?
Hibernate:
select ...from mkyong.stock_daily_record
where stockdaily0_.STOCK_ID=?
批量大小在这里没有任何作用,它不是批量大小如何工作的。见此说法。
批量获取策略没有定义集合中有多少记录被加载。相反,它定义了应该加载多少个集合。
—重复 N 次,直到你记住这句话—
另一个例子
再看一个例子,你想把所有的股票记录及其相关的股票日报表(集合)一个一个打印出来。
List<Stock> list = session.createQuery("from Stock").list();
for(Stock stock : list){
Set sets = stock.getStockDailyRecords();
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
StockDailyRecord sdr = (StockDailyRecord) iter.next();
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
}
没有批量提取策略
输出
Hibernate:
select ...
from mkyong.stock stock0_
Hibernate:
select ...
from mkyong.stock_daily_record stockdaily0_
where stockdaily0_.STOCK_ID=?
Hibernate:
select ...
from mkyong.stock_daily_record stockdaily0_
where stockdaily0_.STOCK_ID=?
Keep repeat the select statements....depend how many stock records in your table.
如果数据库中有 20 条股票记录,Hibernate 的默认获取策略将生成 20+1 条 select 语句并命中数据库。
1.Select 语句检索所有股票记录。
2。选择其相关集合
3。选择其相关收藏
4。选择其相关集合
…。
21。选择其相关集合
生成的查询效率不高,导致了严重的性能问题。
启用了 batch-size='10 '提取策略
让我们看另一个例子,batch-size='10 '是启用的。
输出
Hibernate:
select ...
from mkyong.stock stock0_
Hibernate:
select ...
from mkyong.stock_daily_record stockdaily0_
where
stockdaily0_.STOCK_ID in (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
现在,Hibernate 将使用 select in语句预先获取集合。如果您有 20 条股票记录,它将生成 3 条 select 语句。
1.Select 语句检索所有股票记录。
2。Select In 语句预取其相关集合(一次 10 个集合)
3。Select In 语句预取其相关集合(一次 10 个集合)
启用批量大小后,它将 select 语句从 21 条 select 语句简化为 3 条 select 语句。
4.fetch="subselect "或@Fetch(FetchMode。子选择)
这种获取策略是在一个 sub select 语句中启用其所有相关集合。让我们再次看到相同的查询…
List<Stock> list = session.createQuery("from Stock").list();
for(Stock stock : list){
Set sets = stock.getStockDailyRecords();
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
StockDailyRecord sdr = (StockDailyRecord) iter.next();
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
}
输出
Hibernate:
select ...
from mkyong.stock stock0_
Hibernate:
select ...
from
mkyong.stock_daily_record stockdaily0_
where
stockdaily0_.STOCK_ID in (
select
stock0_.STOCK_ID
from
mkyong.stock stock0_
)
启用“子选择”后,它将创建两个 select 语句。
1.Select 语句检索所有股票记录。
2。在子选择查询中选择其所有相关集合。
结论
抓取策略非常灵活,是优化 Hibernate 查询的一个非常重要的调整,但是如果用错了地方,那将是一场灾难。
参考
1.http://docs . JBoss . org/hibernate/core/3.3/reference/en/html/performance . html
2 .https://www.hibernate.org/315.html
Hibernate 拦截器示例–审计日志
Hibernate 有一个强大的特性叫做“拦截器”,用来拦截或挂钩不同类型的 Hibernate 事件,比如数据库 CRUD 操作。在本文中,我将演示如何使用 Hibernate 拦截器实现一个应用程序审计日志特性,它将把所有 Hibernate 的保存、更新或删除操作记录到一个名为“审计日志的数据库表中。
Hibernate 拦截器示例–审计日志
1.创建表格
创建一个名为“审计日志”的表来存储所有应用程序审计记录。
DROP TABLE IF EXISTS `mkyong`.`auditlog`;
CREATE TABLE `mkyong`.`auditlog` (
`AUDIT_LOG_ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`ACTION` varchar(100) NOT NULL,
`DETAIL` text NOT NULL,
`CREATED_DATE` date NOT NULL,
`ENTITY_ID` bigint(20) unsigned NOT NULL,
`ENTITY_NAME` varchar(255) NOT NULL,
PRIMARY KEY (`AUDIT_LOG_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
2.创建标记界面
创建一个标记接口,任何实现这个接口的类都将被审计。这个接口要求实现的类公开它的标识符-**getId()**和要记录的内容-getLogDeatil()。所有暴露的数据将被存储到数据库中。
package com.mkyong.interceptor;
//market interface
public interface IAuditLog {
public Long getId();
public String getLogDeatil();
}
3.映射“审计日志”表
要与表“审计日志”映射的普通注释模型文件。
@Entity
@Table(name = "auditlog", catalog = "mkyong")
public class AuditLog implements java.io.Serializable {
private Long auditLogId;
private String action;
private String detail;
private Date createdDate;
private long entityId;
private String entityName;
...
}
4.一个类实现了 IAuditLog
一个普通的注释模型文件,用于映射表“stock ”,稍后将用于截取程序演示。它必须实现 IAuditLog 标记接口,并实现 getId() 和 getLogDeatil() 方法。
...
@Entity
@Table(name = "stock", catalog = "mkyong"
public class Stock implements java.io.Serializable, IAuditLog {
...
@Transient
@Override
public Long getId(){
return this.stockId.longValue();
}
@Transient
@Override
public String getLogDeatil(){
StringBuilder sb = new StringBuilder();
sb.append(" Stock Id : ").append(stockId)
.append(" Stock Code : ").append(stockCode)
.append(" Stock Name : ").append(stockName);
return sb.toString();
}
...
5.创建一个助手类
一个助手类,用于接收拦截器的数据,并将其存储到数据库中。
...
public class AuditLogUtil{
public static void LogIt(String action,
IAuditLog entity, Connection conn ){
Session tempSession = HibernateUtil.getSessionFactory().openSession(conn);
try {
AuditLog auditRecord = new AuditLog(action,entity.getLogDeatil()
, new Date(),entity.getId(), entity.getClass().toString());
tempSession.save(auditRecord);
tempSession.flush();
} finally {
tempSession.close();
}
}
}
6.创建一个 Hibernate 拦截器类
通过扩展 Hibernateempty interceptor创建一个拦截器类。下面是最流行的拦截器函数。
- on save–在保存对象时调用,对象尚未保存到数据库中。
- onflush dirty–当您更新一个对象时调用,该对象尚未更新到数据库中。
- on delete–当您删除一个对象时调用,该对象尚未被删除到数据库中。
- 预刷新–在保存、更新或删除的对象提交到数据库之前调用(通常在后刷新之前)。
- post flush–在保存、更新或删除的对象提交到数据库后调用。
代码相当冗长,它应该是自我探索的。
...
public class AuditLogInterceptor extends EmptyInterceptor{
Session session;
private Set inserts = new HashSet();
private Set updates = new HashSet();
private Set deletes = new HashSet();
public void setSession(Session session) {
this.session=session;
}
public boolean onSave(Object entity,Serializable id,
Object[] state,String[] propertyNames,Type[] types)
throws CallbackException {
System.out.println("onSave");
if (entity instanceof IAuditLog){
inserts.add(entity);
}
return false;
}
public boolean onFlushDirty(Object entity,Serializable id,
Object[] currentState,Object[] previousState,
String[] propertyNames,Type[] types)
throws CallbackException {
System.out.println("onFlushDirty");
if (entity instanceof IAuditLog){
updates.add(entity);
}
return false;
}
public void onDelete(Object entity, Serializable id,
Object[] state, String[] propertyNames,
Type[] types) {
System.out.println("onDelete");
if (entity instanceof IAuditLog){
deletes.add(entity);
}
}
//called before commit into database
public void preFlush(Iterator iterator) {
System.out.println("preFlush");
}
//called after committed into database
public void postFlush(Iterator iterator) {
System.out.println("postFlush");
try{
for (Iterator it = inserts.iterator(); it.hasNext();) {
IAuditLog entity = (IAuditLog) it.next();
System.out.println("postFlush - insert");
AuditLogUtil.LogIt("Saved",entity, session.connection());
}
for (Iterator it = updates.iterator(); it.hasNext();) {
IAuditLog entity = (IAuditLog) it.next();
System.out.println("postFlush - update");
AuditLogUtil.LogIt("Updated",entity, session.connection());
}
for (Iterator it = deletes.iterator(); it.hasNext();) {
IAuditLog entity = (IAuditLog) it.next();
System.out.println("postFlush - delete");
AuditLogUtil.LogIt("Deleted",entity, session.connection());
}
} finally {
inserts.clear();
updates.clear();
deletes.clear();
}
}
}
7.启用拦截器
您可以通过将拦截器作为参数传递给 openSession(拦截器)来启用拦截器;。
...
Session session = null;
Transaction tx = null;
try {
AuditLogInterceptor interceptor = new AuditLogInterceptor();
session = HibernateUtil.getSessionFactory().openSession(interceptor);
interceptor.setSession(session);
//test insert
tx = session.beginTransaction();
Stock stockInsert = new Stock();
stockInsert.setStockCode("1111");
stockInsert.setStockName("mkyong");
session.saveOrUpdate(stockInsert);
tx.commit();
//test update
tx = session.beginTransaction();
Query query = session.createQuery("from Stock where stockCode = '1111'");
Stock stockUpdate = (Stock)query.list().get(0);
stockUpdate.setStockName("mkyong-update");
session.saveOrUpdate(stockUpdate);
tx.commit();
//test delete
tx = session.beginTransaction();
session.delete(stockUpdate);
tx.commit();
} catch (RuntimeException e) {
try {
tx.rollback();
} catch (RuntimeException rbe) {
// log.error("Couldn’t roll back transaction", rbe);
}
throw e;
} finally {
if (session != null) {
session.close();
}
}
...
在插入测试中
session.saveOrUpdate(stockInsert); //it will call onSave
tx.commit(); // it will call preFlush follow by postFlush
在更新测试中
session.saveOrUpdate(stockUpdate); //it will call onFlushDirty
tx.commit(); // it will call preFlush follow by postFlush
在删除测试中
session.delete(stockUpdate); //it will call onDelete
tx.commit(); // it will call preFlush follow by postFlush
输出
onSave
Hibernate:
insert into mkyong.stock
(STOCK_CODE, STOCK_NAME)
values (?, ?)
preFlush
postFlush
postFlush - insert
Hibernate:
insert into mkyong.auditlog
(ACTION, CREATED_DATE, DETAIL, ENTITY_ID, ENTITY_NAME)
values (?, ?, ?, ?, ?)
preFlush
Hibernate:
select ...
from mkyong.stock stock0_
where stock0_.STOCK_CODE='1111'
preFlush
onFlushDirty
Hibernate:
update mkyong.stock
set STOCK_CODE=?, STOCK_NAME=?
where STOCK_ID=?
postFlush
postFlush - update
Hibernate:
insert into mkyong.auditlog
(ACTION, CREATED_DATE, DETAIL, ENTITY_ID, ENTITY_NAME)
values (?, ?, ?, ?, ?)
onDelete
preFlush
Hibernate:
delete from mkyong.stock where STOCK_ID=?
postFlush
postFlush - delete
Hibernate:
insert into mkyong.auditlog
(ACTION, CREATED_DATE, DETAIL, ENTITY_ID, ENTITY_NAME)
values (?, ?, ?, ?, ?)
在数据库中
SELECT * FROM auditlog a;
所有审计数据都被插入数据库。
结论
审计日志是一个有用的特性,通常在数据库中使用触发器来处理,但是出于可移植性的考虑,我建议使用应用程序来实现它。
Download this example – Hibernate interceptor example.zipTags : hibernate interceptor
hibernate–多对多示例–连接表+额外列(注释)
在本教程中,我们将向您展示如何使用 Hibernate 来实现“多对多表关系,在连接表中有额外的列”。
Note
For many to many relationship with NO extra column in the join table, please refer to this @many-to-many tutorial
1.多对多表+连接表中的额外列
股票和类别的多对多关系与名为 STOCK_CATEGORY 的第三个/ join 表相链接,并带有额外的“created_by
”和“created_date
”列。
MySQL 表格脚本
CREATE TABLE `stock` (
`STOCK_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`STOCK_CODE` VARCHAR(10) NOT NULL,
`STOCK_NAME` VARCHAR(20) NOT NULL,
PRIMARY KEY (`STOCK_ID`) USING BTREE,
UNIQUE KEY `UNI_STOCK_NAME` (`STOCK_NAME`),
UNIQUE KEY `UNI_STOCK_ID` (`STOCK_CODE`) USING BTREE
)
CREATE TABLE `category` (
`CATEGORY_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`NAME` VARCHAR(10) NOT NULL,
`DESC` VARCHAR(255) NOT NULL,
PRIMARY KEY (`CATEGORY_ID`) USING BTREE
)
CREATE TABLE `stock_category` (
`STOCK_ID` INT(10) UNSIGNED NOT NULL,
`CATEGORY_ID` INT(10) UNSIGNED NOT NULL,
`CREATED_DATE` DATE NOT NULL,
`CREATED_BY` VARCHAR(10) NOT NULL,
PRIMARY KEY (`STOCK_ID`,`CATEGORY_ID`),
CONSTRAINT `FK_CATEGORY_ID` FOREIGN KEY (`CATEGORY_ID`)
REFERENCES `category` (`CATEGORY_ID`),
CONSTRAINT `FK_STOCK_ID` FOREIGN KEY (`STOCK_ID`)
REFERENCES `stock` (`STOCK_ID`)
)
2.项目结构
查看本教程的文件项目结构。
3.Hibernate / JPA 注释
Hibernate / JBoss 工具生成的注释代码在第三个表额外列场景中不起作用。为了让它工作,您应该定制代码,在StockCategory.java
中使用“@AssociationOverride
”来表示多对多关系。
请参见以下自定义代码:
文件:Stock.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
@UniqueConstraint(columnNames = "STOCK_NAME"),
@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {
private Integer stockId;
private String stockCode;
private String stockName;
private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0);
public Stock() {
}
public Stock(String stockCode, String stockName) {
this.stockCode = stockCode;
this.stockName = stockName;
}
public Stock(String stockCode, String stockName,
Set<StockCategory> stockCategories) {
this.stockCode = stockCode;
this.stockName = stockName;
this.stockCategories = stockCategories;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "STOCK_ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
@Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
public String getStockCode() {
return this.stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
@Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
public String getStockName() {
return this.stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.stock", cascade=CascadeType.ALL)
public Set<StockCategory> getStockCategories() {
return this.stockCategories;
}
public void setStockCategories(Set<StockCategory> stockCategories) {
this.stockCategories = stockCategories;
}
}
文件:StockCategory.java
package com.mkyong.stock;
import java.util.Date;
import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
@Entity
@Table(name = "stock_category", catalog = "mkyongdb")
@AssociationOverrides({
@AssociationOverride(name = "pk.stock",
joinColumns = @JoinColumn(name = "STOCK_ID")),
@AssociationOverride(name = "pk.category",
joinColumns = @JoinColumn(name = "CATEGORY_ID")) })
public class StockCategory implements java.io.Serializable {
private StockCategoryId pk = new StockCategoryId();
private Date createdDate;
private String createdBy;
public StockCategory() {
}
@EmbeddedId
public StockCategoryId getPk() {
return pk;
}
public void setPk(StockCategoryId pk) {
this.pk = pk;
}
@Transient
public Stock getStock() {
return getPk().getStock();
}
public void setStock(Stock stock) {
getPk().setStock(stock);
}
@Transient
public Category getCategory() {
return getPk().getCategory();
}
public void setCategory(Category category) {
getPk().setCategory(category);
}
@Temporal(TemporalType.DATE)
@Column(name = "CREATED_DATE", nullable = false, length = 10)
public Date getCreatedDate() {
return this.createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
@Column(name = "CREATED_BY", nullable = false, length = 10)
public String getCreatedBy() {
return this.createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
StockCategory that = (StockCategory) o;
if (getPk() != null ? !getPk().equals(that.getPk())
: that.getPk() != null)
return false;
return true;
}
public int hashCode() {
return (getPk() != null ? getPk().hashCode() : 0);
}
}
文件:StockCategoryId.java
package com.mkyong.stock;
import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;
@Embeddable
public class StockCategoryId implements java.io.Serializable {
private Stock stock;
private Category category;
@ManyToOne
public Stock getStock() {
return stock;
}
public void setStock(Stock stock) {
this.stock = stock;
}
@ManyToOne
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StockCategoryId that = (StockCategoryId) o;
if (stock != null ? !stock.equals(that.stock) : that.stock != null) return false;
if (category != null ? !category.equals(that.category) : that.category != null)
return false;
return true;
}
public int hashCode() {
int result;
result = (stock != null ? stock.hashCode() : 0);
result = 31 * result + (category != null ? category.hashCode() : 0);
return result;
}
}
文件:Category.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "category", catalog = "mkyongdb")
public class Category implements java.io.Serializable {
private Integer categoryId;
private String name;
private String desc;
private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0);
public Category() {
}
public Category(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Category(String name, String desc, Set<StockCategory> stockCategories) {
this.name = name;
this.desc = desc;
this.stockCategories = stockCategories;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "CATEGORY_ID", unique = true, nullable = false)
public Integer getCategoryId() {
return this.categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
@Column(name = "NAME", nullable = false, length = 10)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "[DESC]", nullable = false)
public String getDesc() {
return this.desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.category")
public Set<StockCategory> getStockCategories() {
return this.stockCategories;
}
public void setStockCategories(Set<StockCategory> stockCategories) {
this.stockCategories = stockCategories;
}
}
完成后,多对多关系现在应该可以工作了。
4.运行它–案例 1
一个新的类别和新的股票。
session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7052");
stock.setStockName("PADINI");
Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
//new category, need save to get the id first
session.save(category1);
StockCategory stockCategory = new StockCategory();
stockCategory.setStock(stock);
stockCategory.setCategory(category1);
stockCategory.setCreatedDate(new Date()); //extra column
stockCategory.setCreatedBy("system"); //extra column
stock.getStockCategories().add(stockCategory);
session.save(stock);
session.getTransaction().commit();
输出…
Hibernate:
insert
into
mkyongdb.category
(`DESC`, NAME)
values
(?, ?)
Hibernate:
insert
into
mkyongdb.stock
(STOCK_CODE, STOCK_NAME)
values
(?, ?)
Hibernate:
select
stockcateg_.CATEGORY_ID,
stockcateg_.STOCK_ID,
stockcateg_.CREATED_BY as CREATED1_2_,
stockcateg_.CREATED_DATE as CREATED2_2_
from
mkyongdb.stock_category stockcateg_
where
stockcateg_.CATEGORY_ID=?
and stockcateg_.STOCK_ID=?
Hibernate:
insert
into
mkyongdb.stock_category
(CREATED_BY, CREATED_DATE, CATEGORY_ID, STOCK_ID)
values
(?, ?, ?, ?)
5.运行它–案例 2
获取现有类别和新股票。
session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7052");
stock.setStockName("PADINI");
//assume category id is 7
Category category1 = (Category)session.get(Category.class, 7);
StockCategory stockCategory = new StockCategory();
stockCategory.setStock(stock);
stockCategory.setCategory(category1);
stockCategory.setCreatedDate(new Date()); //extra column
stockCategory.setCreatedBy("system"); //extra column
stock.getStockCategories().add(stockCategory);
session.save(stock);
session.getTransaction().commit();
输出…
Hibernate:
select
category0_.CATEGORY_ID as CATEGORY1_1_0_,
category0_.`DESC` as DESC2_1_0_,
category0_.NAME as NAME1_0_
from
mkyongdb.category category0_
where
category0_.CATEGORY_ID=?
Hibernate:
insert
into
mkyongdb.stock
(STOCK_CODE, STOCK_NAME)
values
(?, ?)
Hibernate:
select
stockcateg_.CATEGORY_ID,
stockcateg_.STOCK_ID,
stockcateg_.CREATED_BY as CREATED1_2_,
stockcateg_.CREATED_DATE as CREATED2_2_
from
mkyongdb.stock_category stockcateg_
where
stockcateg_.CATEGORY_ID=?
and stockcateg_.STOCK_ID=?
Hibernate:
insert
into
mkyongdb.stock_category
(CREATED_BY, CREATED_DATE, CATEGORY_ID, STOCK_ID)
values
(?, ?, ?, ?)
完成了。
Download it – Hibernate-many-to-many-third-table-annotation.zip (13KB)
参考
Tags : hibernate many-to-many
hibernate–多对多示例(注释)
在本教程中,它将重用之前的“Hibernate many to many example–XML mapping”教程的整个基础设施,并对其进行增强以支持 Hibernare / JPA 注释。
Note
For many to many with extra columns in join table, please refer to this tutorial.
项目结构
查看本教程的新项目结构。
## 1.“多对多”表关系
再看前面的多对多表关系。
## 2.Hibernate 模型类
更新以前的模型类—Stock.java
和Category.java
,并在其中定义新的注释代码。
文件:Stock.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.CascadeType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
@UniqueConstraint(columnNames = "STOCK_NAME"),
@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {
private Integer stockId;
private String stockCode;
private String stockName;
private Set<Category> categories = new HashSet<Category>(0);
public Stock() {
}
public Stock(String stockCode, String stockName) {
this.stockCode = stockCode;
this.stockName = stockName;
}
public Stock(String stockCode, String stockName, Set<Category> categories) {
this.stockCode = stockCode;
this.stockName = stockName;
this.categories = categories;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "STOCK_ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
@Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
public String getStockCode() {
return this.stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
@Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
public String getStockName() {
return this.stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "stock_category", catalog = "mkyongdb", joinColumns = {
@JoinColumn(name = "STOCK_ID", nullable = false, updatable = false) },
inverseJoinColumns = {
@JoinColumn(name = "CATEGORY_ID",
nullable = false, updatable = false) })
public Set<Category> getCategories() {
return this.categories;
}
public void setCategories(Set<Category> categories) {
this.categories = categories;
}
}
文件:Category.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
@Entity
@Table(name = "category", catalog = "mkyongdb")
public class Category implements java.io.Serializable {
private Integer categoryId;
private String name;
private String desc;
private Set<Stock> stocks = new HashSet<Stock>(0);
public Category() {
}
public Category(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Category(String name, String desc, Set<Stock> stocks) {
this.name = name;
this.desc = desc;
this.stocks = stocks;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "CATEGORY_ID", unique = true, nullable = false)
public Integer getCategoryId() {
return this.categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
@Column(name = "NAME", nullable = false, length = 10)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "[DESC]", nullable = false)
public String getDesc() {
return this.desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
public Set<Stock> getStocks() {
return this.stocks;
}
public void setStocks(Set<Stock> stocks) {
this.stocks = stocks;
}
}
3.休眠配置文件
将带注释的类Stock.java
和Category.java
放在hibernate.cfg.xml
中,如下所示:
文件:hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyongdb</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">password</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<mapping class="com.mkyong.stock.Stock" />
<mapping class="com.mkyong.stock.Category" />
</session-factory>
</hibernate-configuration>
4.运行它
运行它,结果不言自明。
文件:App.java
package com.mkyong;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import com.mkyong.stock.Category;
import com.mkyong.stock.Stock;
import com.mkyong.util.HibernateUtil;
public class App {
public static void main(String[] args) {
System.out.println("Hibernate many to many (Annotation)");
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7052");
stock.setStockName("PADINI");
Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
Category category2 = new Category("INVESTMENT", "INVESTMENT COMPANY");
Set<Category> categories = new HashSet<Category>();
categories.add(category1);
categories.add(category2);
stock.setCategories(categories);
session.save(stock);
session.getTransaction().commit();
System.out.println("Done");
}
}
输出
Hibernate many to many (Annotation)
Hibernate:
insert
into
mkyongdb.stock
(STOCK_CODE, STOCK_NAME)
values
(?, ?)
Hibernate:
insert
into
mkyongdb.category
(`DESC`, NAME)
values
(?, ?)
Hibernate:
insert
into
mkyongdb.category
(`DESC`, NAME)
values
(?, ?)
Hibernate:
insert
into
mkyongdb.stock_category
(STOCK_ID, CATEGORY_ID)
values
(?, ?)
Hibernate:
insert
into
mkyongdb.stock_category
(STOCK_ID, CATEGORY_ID)
values
(?, ?)
Done
Download it – Hibernate-many-to-many-annotation.zip (9KB)
参考
- Hibernate 文档–多对多关系。
hibernate–多对多示例(XML 映射)
当一个实体中的每个记录在另一个实体中可能有许多链接的记录时,就会出现多对多关系,反之亦然。
在本教程中,我们将向您展示如何通过 XML 映射文件(hbm)在 Hibernate 中处理多对多表关系。
Note
For many to many with extra columns in join table, please refer to this tutorial.
本教程中使用的工具和技术:
- Hibernate 3.6.3 .最终版
- MySQL 5.1.15
- Maven 3.0.3
- Eclipse 3.6
项目结构
本教程的项目结构。
项目依赖性
从 JBoss 存储库中获取最新的 hibernate.jar 。
文件:pom.xml
<project ...>
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!-- MySQL database driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.15</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.3.Final</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
</dependencies>
</project>
1.“多对多”示例
这是一个多对多的关系表设计,一个股票表有多个类别,而类别可以属于多个股票,这种关系与第三个名为 STOCK_CATEGORY 的表相链接。
表 STOCK_CATEGORY 只包含两个主键,以及引用 STOCK 和 CATEGORY 外键。
MySQL 表格脚本
CREATE TABLE `stock` (
`STOCK_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`STOCK_CODE` varchar(10) NOT NULL,
`STOCK_NAME` varchar(20) NOT NULL,
PRIMARY KEY (`STOCK_ID`) USING BTREE,
UNIQUE KEY `UNI_STOCK_NAME` (`STOCK_NAME`),
UNIQUE KEY `UNI_STOCK_ID` (`STOCK_CODE`) USING BTREE
)
CREATE TABLE `category` (
`CATEGORY_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`NAME` varchar(10) NOT NULL,
`DESC` varchar(255) NOT NULL,
PRIMARY KEY (`CATEGORY_ID`) USING BTREE
)
CREATE TABLE `stock_category` (
`STOCK_ID` int(10) unsigned NOT NULL,
`CATEGORY_ID` int(10) unsigned NOT NULL,
PRIMARY KEY (`STOCK_ID`,`CATEGORY_ID`),
CONSTRAINT `FK_CATEGORY_ID` FOREIGN KEY (`CATEGORY_ID`) REFERENCES `category` (`CATEGORY_ID`),
CONSTRAINT `FK_STOCK_ID` FOREIGN KEY (`STOCK_ID`) REFERENCES `stock` (`STOCK_ID`)
)
2.Hibernate 模型类
创建两个模型类—Stock.java
和Category.java
,来表示上面的表。不需要为表’ stock_category 创建额外的类。
文件:Stock.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
public class Stock implements java.io.Serializable {
private Integer stockId;
private String stockCode;
private String stockName;
private Set<Category> categories = new HashSet<Category>(0);
//getter, setter and constructor
}
文件:Category.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
public class Category implements java.io.Serializable {
private Integer categoryId;
private String name;
private String desc;
private Set<Stock> stocks = new HashSet<Stock>(0);
//getter, setter and constructor
}
3.Hibernate XML 映射
现在,创建两个 Hibernate 映射文件(hbm)—Stock.hbm.xml
和Category.hbm.xml
。您会注意到第三个’股票 _ 类别’表是通过“多对多标记引用的。
文件:Stock.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.mkyong.stock.Stock" table="stock" catalog="mkyongdb">
<id name="stockId" type="java.lang.Integer">
<column name="STOCK_ID" />
<generator class="identity" />
</id>
<property name="stockCode" type="string">
<column name="STOCK_CODE" length="10" not-null="true" unique="true" />
</property>
<property name="stockName" type="string">
<column name="STOCK_NAME" length="20" not-null="true" unique="true" />
</property>
<set name="categories" table="stock_category"
inverse="false" lazy="true" fetch="select" cascade="all" >
<key>
<column name="STOCK_ID" not-null="true" />
</key>
<many-to-many entity-name="com.mkyong.stock.Category">
<column name="CATEGORY_ID" not-null="true" />
</many-to-many>
</set>
</class>
</hibernate-mapping>
文件:Category.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.mkyong.stock.Category" table="category" catalog="mkyongdb">
<id name="categoryId" type="java.lang.Integer">
<column name="CATEGORY_ID" />
<generator class="identity" />
</id>
<property name="name" type="string">
<column name="NAME" length="10" not-null="true" />
</property>
<property name="desc" type="string">
<column name="[DESC]" not-null="true" />
</property>
<set name="stocks" table="stock_category" inverse="true" lazy="true" fetch="select">
<key>
<column name="CATEGORY_ID" not-null="true" />
</key>
<many-to-many entity-name="com.mkyong.stock.Stock">
<column name="STOCK_ID" not-null="true" />
</many-to-many>
</set>
</class>
</hibernate-mapping>
4.休眠配置文件
现在,将Stock.hbm.xml
和Category.hbm.xml
以及 MySQL 细节放在hibernate.cfg.xml
中。
文件:hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyongdb</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">password</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<mapping resource="com/mkyong/stock/Stock.hbm.xml" />
<mapping resource="com/mkyong/stock/Category.hbm.xml" />
</session-factory>
</hibernate-configuration>
5.运行它
运行它,Hibernate 将在股票表中插入一条记录,在类别表中插入两条记录,在股票类别表中也插入两条记录。
文件:App.java
package com.mkyong;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import com.mkyong.stock.Category;
import com.mkyong.stock.Stock;
import com.mkyong.util.HibernateUtil;
public class App {
public static void main(String[] args) {
System.out.println("Hibernate many to many (XML Mapping)");
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7052");
stock.setStockName("PADINI");
Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
Category category2 = new Category("INVESTMENT", "INVESTMENT COMPANY");
Set<Category> categories = new HashSet<Category>();
categories.add(category1);
categories.add(category2);
stock.setCategories(categories);
session.save(stock);
session.getTransaction().commit();
System.out.println("Done");
}
}
输出…结果应该是不言自明的
Hibernate many to many (XML Mapping)
Hibernate:
insert
into
mkyongdb.stock
(STOCK_CODE, STOCK_NAME)
values
(?, ?)
Hibernate:
insert
into
mkyongdb.category
(NAME, `DESC`)
values
(?, ?)
Hibernate:
insert
into
mkyongdb.category
(NAME, `DESC`)
values
(?, ?)
Hibernate:
insert
into
stock_category
(STOCK_ID, CATEGORY_ID)
values
(?, ?)
Hibernate:
insert
into
stock_category
(STOCK_ID, CATEGORY_ID)
values
(?, ?)
Done
Hibernate Annotation
For many-to-many in Hibernate annotation, please refer to this example.Download it – Hibernate-many-to-many-xml-mapping.zip (10KB)
参考
- Hibernate 文档–多对多关系。
Tags : hibernate many-to-many
Hibernate 可变示例(类和集合)
在 hibernate 中,类及其相关集合中的“可变的”默认为“真”,这意味着允许添加、更新和删除类或集合。另一方面,如果可变变量被更改为 false,它在类及其相关集合中具有不同的含义。下面我们举几个例子来深入了解一下。
Hibernate 一对多示例
我将把这个一对多示例用于可变演示。在这个映射文件中,一个股票属于许多 StockDailyRecord。
<!-- Stock.hbm.xml -->
...
<hibernate-mapping>
<class name="com.mkyong.common.Stock" table="stock" >
<set name="stockDailyRecords" mutable="false" cascade="all"
inverse="true" lazy="true" table="stock_daily_record">
<key>
<column name="STOCK_ID" not-null="true" />
</key>
<one-to-many class="com.mkyong.common.StockDailyRecord" />
</set>
</class>
...
</hibernate-mapping>
如何声明 mutable?
XML 映射文件和注释都支持“可变”。
1.XML 映射文件
在映射文件中,‘可变关键字用于实现可变函数。
<!-- Stock.hbm.xml -->
...
<hibernate-mapping>
<class name="com.mkyong.common.Stock" table="stock" mutable="false" >
<set name="stockDailyRecords" mutable="false" cascade="all"
inverse="true" lazy="true" table="stock_daily_record" >
<key>
<column name="STOCK_ID" not-null="true" />
</key>
<one-to-many class="com.mkyong.common.StockDailyRecord" />
</set>
</class>
...
</hibernate-mapping>
2.注释
在注释中,关键字被更改为@Immutable (mutable='false ')。
...
@Entity
@Immutable
@Table(name = "stock", catalog = "mkyong")
public class Stock implements java.io.Serializable {
...
@OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
@Immutable
public Set<StockDailyRecord> getStockDailyRecords() {
return this.stockDailyRecords;
}
...
在类中可变
如果在 class 元素中声明了 mutable = "false "或@Immutable,这意味着对该类的更新将被忽略,但不会抛出异常,只允许添加和删除操作。
1.测试插件
Stock stock = new Stock();
stock.setStockCode("7277");
stock.setStockName("DIALOG");
session.save(stock);
如果在类中声明了 mutable = "true "(默认)或 no @Immutable。
输出
Hibernate:
insert into mkyong.stock (STOCK_CODE, STOCK_NAME)
values (?, ?)
如果在类中声明了 mutable = "false "或@Immutable。
输出
Hibernate:
insert into mkyong.stock (STOCK_CODE, STOCK_NAME)
values (?, ?)
类中的可变变量对“插入”操作没有影响。
2.测试更新
Stock stock = (Stock)session.createQuery(
" from Stock where stockCode = '7277'").list().get(0);
stock.setStockName("DIALOG123");
session.saveOrUpdate(stock);
如果在类中声明了 mutable = "true "或 no @Immutable。
输出
Hibernate:
select ...from mkyong.stock stock0_
where stock0_.STOCK_CODE='7277'
Hibernate:
update mkyong.stock
set STOCK_CODE=?,STOCK_NAME=?
where STOCK_ID=?
如果在类中声明了 mutable = "false "或@Immutable。
输出
Hibernate:
select ...from mkyong.stock stock0_
where stock0_.STOCK_CODE='7277'
类中的可变变量不允许应用程序对其进行更新,“更新”操作将被忽略,并且不会引发异常
3.测试删除
Stock stock = (Stock)session.createQuery(
" from Stock where stockCode = '7277'").list().get(0);
session.delete(stock);
如果在类中声明了 mutable = "true "(默认)或 no @Immutable。
输出
Hibernate:
delete from mkyong.stock
where STOCK_ID=?
如果在类中声明了 mutable = "false "或@Immutable。
输出
Hibernate:
delete from mkyong.stock
where STOCK_ID=?
类中的可变变量在“删除”操作中无效。
集合中可变的
如果在集合中声明了 mutable = "false "或@Immutable,这意味着在这个集合中不允许使用 add 和 delete-orphan,只有 update 和’ cascade delete all '是允许的。
1.测试插件
假设级联插入已启用。
Stock stock = (Stock)session.createQuery(
" from Stock where stockCode = '7277'").list().get(0);
StockDailyRecord sdr = new StockDailyRecord();
sdr.setDate(new Date());
sdr.setStock(stock);
stock.getStockDailyRecords().add(sdr);
session.save(stock);
如果在集合中声明了 mutable = "true "(默认值)或 no @Immutable。
输出
Hibernate:
insert into mkyong.stock_daily_record
(STOCK_ID, PRICE_OPEN, PRICE_CLOSE, PRICE_CHANGE, VOLUME, DATE)
values (?, ?, ?, ?, ?, ?)
如果在集合中声明了 mutable = "false "或@Immutable。
输出
Exception in thread "main" org.hibernate.HibernateException:
changed an immutable collection instance:
[com.mkyong.common.Stock.stockDailyRecords#111]
集合中的 Mutable 不允许’ add '操作,会抛出异常。
2.测试更新
假设级联更新已启用。
Stock stock = (Stock)session.createQuery(
" from Stock where stockCode = '7277'").list().get(0);
StockDailyRecord sdr = stock.getStockDailyRecords().iterator().next();
sdr.setPriceChange(new Float(1.30));
session.saveOrUpdate(stock);
如果在集合中声明了 mutable = "true "(默认值)或 no @Immutable。
输出
Hibernate:
update mkyong.stock_daily_record
set PRICE_CHANGE=?, ...
where DAILY_RECORD_ID=?
如果在集合中声明了 mutable = "false "或@Immutable。
输出
Hibernate:
update mkyong.stock_daily_record
set PRICE_CHANGE=?, ...
where DAILY_RECORD_ID=?
集合中的可变变量对“更新”操作没有影响。
3.测试删除-孤立
假设级联删除-孤立被启用。
Stock stock = (Stock)session.createQuery(
" from Stock where stockCode = '7277'").list().get(0);
StockDailyRecord sdr = stock.getStockDailyRecords().iterator().next();
stock.getStockDailyRecords().remove(sdr);
session.saveOrUpdate(stock);
如果在集合中声明了 mutable = "true "(默认值)或 no @Immutable。
输出
Hibernate:
delete from mkyong.stock_daily_record
where DAILY_RECORD_ID=?
如果在集合中声明了 mutable = "false "或@Immutable。
输出
Exception in thread "main" org.hibernate.HibernateException:
changed an immutable collection instance:
[com.mkyong.common.Stock.stockDailyRecords#111]
集合中的可变不允许“删除-孤立”操作,将引发异常。
4.测试删除
假设级联删除已启用。
Stock stock = (Stock)session.createQuery(
" from Stock where stockCode = '7277'").list().get(0);
session.saveOrUpdate(stock);
如果在集合中声明了 mutable = "true "(默认值)或 no @Immutable。
输出
Hibernate:
delete from mkyong.stock_daily_record
where DAILY_RECORD_ID=?
Hibernate:
delete from mkyong.stock
where STOCK_ID=?
如果在集合中声明了 mutable = "false "或@Immutable。
输出
Hibernate:
delete from mkyong.stock_daily_record
where DAILY_RECORD_ID=?
Hibernate:
delete from mkyong.stock
where STOCK_ID=?
集合中的可变元素在“删除”操作中无效,如果父元素被删除,它的所有子元素也会被删除,即使它是可变的。
为什么可变?
Mutable 可以避免许多无意的数据库操作,比如添加、更新或删除一些不应该的记录。此外,根据 Hibernate 文档,mutable do 有一些小的性能优化,建议分析映射关系并根据需要实现 mutable。
摘要
1.在类中声明了 mutable = "false "或@Immutable
这意味着对这个类的更新将被忽略,但是不会抛出异常,只允许添加和删除操作。
在具有 mutable = " false "–insert = allow,delete=allow,update=not allow 的类中
2.集合中声明了 mutable = "false "或@Immutable
这意味着在这个集合中不允许添加和删除孤儿,只允许更新。但是,如果级联删除被启用,当父代被删除时,它的所有子代也会被删除,即使它是可变的。
在集合中使用 mutable = " false "–insert =不允许,delete-orphan =不允许,delete =允许,update =允许
完全不可改变?
一个类可以对任何动作完全不可变吗?是的,把一个 mutable =“false”放在它的所有关系中(insert =不允许,delete-orphan =不允许),把一个 mutable =“false”放在你希望不可变的类中(update =不允许)。现在,您有了一个完全不可变的类,但是,如果级联删除选项被启用,当您的不可变类的父类被删除时,您的不可变类也将被删除。
参考
1.http://docs . JBoss . org/hibernate/stable/annotations/reference/en/html _ single/
Hibernate 命名查询示例
很多时候,开发人员喜欢将 HQL 字符串分散在 Java 代码中,这种方法很难维护,而且看起来很难看。幸运的是,Hibernate 出来了一种叫做“ names queries ”的技术,它让开发者把所有的 HQL 放到 XML 映射文件中或者通过注释。
如何声明命名查询
HQL 或本机 SQL 都支持命名查询。查看示例…
1.XML 映射文件
映射文件中的 HQL
<!-- stock.hbm.xml -->
<hibernate-mapping>
<class name="com.mkyong.common.Stock" table="stock" ...>
<id name="stockId" type="java.lang.Integer">
<column name="STOCK_ID" />
<generator class="identity" />
</id>
<property name="stockCode" type="string">
<column name="STOCK_CODE" length="10" not-null="true" unique="true" />
</property>
...
</class>
<query name="findStockByStockCode">
<![CDATA[from Stock s where s.stockCode = :stockCode]]>
</query>
</hibernate-mapping>
映射文件中的本机 SQL
<!-- stock.hbm.xml -->
<hibernate-mapping>
<class name="com.mkyong.common.Stock" table="stock" ...>
<id name="stockId" type="java.lang.Integer">
<column name="STOCK_ID" />
<generator class="identity" />
</id>
<property name="stockCode" type="string">
<column name="STOCK_CODE" length="10" not-null="true" unique="true" />
</property>
...
</class>
<sql-query name="findStockByStockCodeNativeSQL">
<return alias="stock" class="com.mkyong.common.Stock"/>
<![CDATA[select * from stock s where s.stock_code = :stockCode]]>
</sql-query>
</hibernate-mapping>
您可以在’ hibernate-mapping 元素中放置一个命名查询,但是不要放在’类元素之前,hibernate 会提示无效的映射文件,您的所有命名查询都必须放在’类元素之后。
Note
Regarding the CDATA , it’s always good practice to wrap your query text with CDATA, so that the XML parser will not prompt error for some special XML characters like ‘>’ , <‘ and etc. ## 2.注释
注释中的 HQL
@NamedQueries({
@NamedQuery(
name = "findStockByStockCode",
query = "from Stock s where s.stockCode = :stockCode"
)
})
@Entity
@Table(name = "stock", catalog = "mkyong")
public class Stock implements java.io.Serializable {
...
批注中的本机 SQL
@NamedNativeQueries({
@NamedNativeQuery(
name = "findStockByStockCodeNativeSQL",
query = "select * from stock s where s.stock_code = :stockCode",
resultClass = Stock.class
)
})
@Entity
@Table(name = "stock", catalog = "mkyong")
public class Stock implements java.io.Serializable {
...
在原生 SQL 中,你必须声明’ resultClass '来让 Hibernate 知道什么是返回类型,否则会导致异常“org . Hibernate . CFG . notyeimplemented exception:尚不支持纯原生标量查询”。
调用命名查询
在 Hibernate 中,可以通过 getNamedQuery 方法调用命名查询。
Query query = session.getNamedQuery("findStockByStockCode")
.setString("stockCode", "7277");
Query query = session.getNamedQuery("findStockByStockCodeNativeSQL")
.setString("stockCode", "7277");
结论
命名查询是全局访问,这意味着查询的名称在 XML 映射文件或注释中必须是唯一的。在实际环境中,将所有命名查询隔离到它们自己的文件中总是一个好的做法。此外,存储在 Hibernate 映射文件或注释中的命名查询比分散在 Java 代码中的查询更容易维护。