以下是具有实体类的两个表。
tbl_rules
| rule_id | rule_name |
@Entity
@Table(name = "db_user_name.tbl_rules")
public class Rule implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@GenericGenerator(name = "incre", strategy = "increment")
@GeneratedValue(generator = "incre")
@Column(name = "rule_id", unique = true, nullable = false)
private long ruleId;
@Column(name = "rule_name", length = 250)
private String ruleName;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "rules")
private Set<Benchmark> benchmarks = new HashSet<Benchmark>(0);
... getters and setters
}
tbl_benchmark
| benchmark_id | rule_id |
@Entity
@Table(name = "tbl_benchmark", catalog = "db_user_name")
@DynamicUpdate(value = true)
public class Benchmark implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@Column(name = "benchmark_id", unique = true, nullable = false)
private Long benchmarkId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rule_id", nullable = false)
private Rule rules;
.. getter and setters
}
在以下情况中遇到sql server db
的死锁问题
HibernateSessionManager.beginTransaction()
; saveRule(rule)
//并对两个表进行基准测试
被锁定(使用sql server锁定表查询)saveBenchmark(benchmark)
//死锁HibernateSessionManager.commit()
; 发生死锁的代码:
HibernateSessionManager.beginTransaction();
UserIdManager.setCurrentGroupId(2);
if (savingObjects.get(AmlConstants.USERCREDENTAILS_STRING) != null){
userCredentials = (UserCredentials) savingObjects.get(AmlConstants.USERCREDENTAILS_STRING);
Util.setAuditLogField(AmlConstants.USERIDSTRING);
this.getAmlDAOFactory().getUserCredentialsDAO().updateUserDetails(userCredentials);
if (savingObjects.get(AmlConstants.USERBRANCHMAPPING_STRING) != null){
userBranchMapping = (UserBranchMapping) savingObjects.get(AmlConstants.USERBRANCHMAPPING_STRING);
Util.setAuditLogField(AmlConstants.BRANCH_STRING);
this.getAmlDAOFactory().getUserBranchMappingDAO().saveUserBranchMapping(userBranchMapping);
}
}
HibernateSessionManager.commit();
saveRule:
@Override
public Rule saveRule(Rule rule) throws Exception {
try {
getSession().saveOrUpdate(rule);
getSession().flush();
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}
return rule;
}
saveBenchmark:
@Override
public Benchmark saveBenchMark(Benchmark benchmark) throws Exception {
try {
if (benchmark.getBenchmarkId() == null)
benchmark.setBenchmarkId(getBenchmarkCount() + 1);
getSession().clear();
getSession().saveOrUpdate(benchmark);
getSession().flush();
} catch (RuntimeException e) {
// logger.error("Runtime error while saving benchmark", e);
e.printStackTrace();
} catch (Exception e) {
logger.error("Exception while saving benchmark " + e.getMessage(), e);
}
return benchmark;
}
Spring-Hib配置文件:
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
..
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">com.aml.hibernate.SQLServerCustomeDialect</prop>
<prop key="hibernate.character_encoding">UTF-8</prop>
<prop key="hibernate.connection.useUnicode">true</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.generate_statistics">false</prop>
</props>
</property>
..
HibernateSessionManager.java
public class HibernateSessionManager {
public static Logger logger = Logger.getLogger(HibernateSessionManager.class);
public static final ThreadLocal<Session> currentSession = new ThreadLocal<Session>();
public static final ThreadLocal<java.util.List<Session>> sessionList = new ThreadLocal<java.util.List<Session>>();
/** Store transaction object on thread local
* this helps to make a request processing transactional */
public static final ThreadLocal<Transaction> transaction = new ThreadLocal<Transaction>();
public static final ThreadLocal<Map<String, Transaction>> transactionMap = new ThreadLocal<Map<String, Transaction>>();
/** keep the beginner method path which helps to commit on the same method only
* we are not supposed to make nested commits under a single request */
public static final ThreadLocal<String> callerXPath = new ThreadLocal<String>();
/**
* Returns existing hibernate session binded with current request thread,
* if no session already bind with current thread then it will open a new session, bind to current thread
* and returns the session object
*
* @param sessionFactory
* @return
* @throws HibernateException
*/
public static Session currentSession(SessionFactory sessionFactory) throws HibernateException {
Session s = (Session) currentSession.get();
// Open a new Session, if this Thread has none yet
if (s == null || !s.isOpen())
{
s = sessionFactory.openSession();
currentSession.set(s);
if(sessionList.get()==null)
sessionList.set(new LinkedList<Session>());
sessionList.get().add(s);
logger.debug("Opened new session:"+currentSession.get().hashCode());
}else{
logger.debug("returning existing session:"+currentSession.get().hashCode());
}
return s;
}
/**
* Closes all the sessions binded with current request thread
* @throws HibernateException
*/
public static void closeSession() throws HibernateException {
currentSession.set(null);
transaction.set(null);
callerXPath.set(null);
try{
if(sessionList.get()!=null)
for (int i = 0; i < sessionList.get().size(); i++) {
Session s = sessionList.get().get(i);
try{
if (s != null && s.isOpen())
s.close();
logger.debug("Closed session - session local:"+ (s!=null?s.hashCode(): ""));
}catch (Exception e) { logger.debug("Error while closing session: ", e); }
}
transactionMap.get().clear();
}catch (Exception e) { logger.debug("Error while closing session: ", e); }
sessionList.set(null);
transactionMap.set(null);
}
// ------------------- Transaction management ------------------
/**
* Starts a new hibernate transaction on the session binded to current request thread
* if there is already a transaction started on this thread, ignores creation of another transaction
* all the db calls on a single request thread has to come under a single transaction
* @return
*/
public static boolean beginTransaction(){
try{
logger.debug("beginTransaction............... ");
Transaction t = transaction.get();
if(t == null && callerXPath.get()==null){
Session s = currentSession.get();
t = s.beginTransaction();
t.registerSynchronization(new Synchronization() {
@Override
public void beforeCompletion() {
logger.debug("Transaction-beforeCompletion............... ");
}
@Override
public void afterCompletion(int status) {
logger.debug("Transaction-afterCompletion............... "+status);
}
});
transaction.set(t);
callerXPath.set(getCallerMethodInvolvedinTransaction());
if(transactionMap.get()==null)
transactionMap.set(new HashMap<String, Transaction>());
transactionMap.get().put(callerXPath.get(), t);
logger.debug("Started new hibernate transaction:"+t);
}
}catch (Exception e) {
logger.error("Error while starting new transaction: ", e);
return false;
}
return true;
}
/**
* if we already have a hibernate transaction created on the current request thread and some thing is committed on it
* it will rollback the changes done after the transaction initialization
*/
public static void rollback(){
try{
Transaction t = transactionMap.get().get(callerXPath.get());
if(t != null){
t.rollback();
logger.debug("Roll back success on transaction:"+t);
}
}catch (Exception e) {
logger.error("Exception while trying to rollback", e);
}
}
/**
* Commits all the changes done after the transaction started on the current request thread
* this accepts the commit command from the only method which started the transaction
* This will unlink the current session and then currentSession() method can give another session as existing one is unlinked on the thread local
*/
public static void commit(){
try{
logger.debug("commit............... ");
Transaction t = transaction.get();
if(t != null /*&& !t.wasCommitted()*/
&& callerXPath.get()!=null && callerXPath.get().equals(getCallerMethodInvolvedinTransaction())){
t.commit();
currentSession.get().clear();
currentSession.set(null);
transaction.set(null);
callerXPath.set(null);
logger.debug("Commit success on transaction:"+t);
}
}catch (Exception e) {
logger.error("Exception while trying to commit", e);
}
}
/**
* get the caller method xpath: <package>.<classname>.<methodname>
* @return
*/
public static String getCallerMethodInvolvedinTransaction() {
try{
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
return stElements[3].toString().split("\\(")[0];
/*for (int i = 3; i < stElements.length; i++) {
String rawFQN = stElements[i].toString().split("\\(")[0];
String className = rawFQN.substring(0, rawFQN.lastIndexOf('.'));
String methodName = rawFQN.substring(rawFQN.lastIndexOf('.')+1);
Object carObj = Class.forName(className).newInstance();
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(className);
CtMethod methodX = cc.getDeclaredMethod(methodName);
int xlineNumber = methodX.getMethodInfo().getLineNumber(0);
Method method = carObj.getClass().getMethod(methodName);
if (method.isAnnotationPresent(JCTransaction.class))
{
return rawFQN;
}
}*/
}catch (Exception e) {
logger.error("" , e);
}
return null;
}
}
但同样适用于oracle db
(使用oracle hib属性)。
答案 0 :(得分:3)
你的代码必须有问题,否则你永远不应该把自己锁在外面。两个不同的连接可以相互阻塞,但是一个连接永远不会阻塞它自己的锁。我没有详细查看代码,我将重点讨论为什么你会遇到SQL Server而不是Oracle的问题。
Oracle总是对行使用版本控制,因此行永远不会因为它们被读取而被锁定。另一方面,SQL Server通常采用读锁定,读锁定将阻止来自其他会话的写入。您可以将SQL Server隔离级别更改为READ_COMMITED_SNAPSHOT以隐藏问题,但它仍然存在。
我不明白为什么要在几个地方清除会话,这几乎不应该完成。我也不理解在HibernateSessionManager中处理事务的所有代码,这可能是问题的根本原因。不知何故,你运行多个交易。保持简单,问题可能会消失!
答案 1 :(得分:2)
根据我的理解,您已经定义了规则与优化之间的OneToMany关系。基准。因此,在构建Rule Entitiy / Object时,您也构建了Benchmark。我对吗?
<强>分析:强> 我假设Benchmark对象也被填充并保存在Set Benchmarks中。所以现在你正在保存规则,因为休眠它也会尝试保存Benchmark。在同一个事务中,您正试图再次保存Benchmark,并且由于此事务管理器处于死锁状态。
<强>解决方案:强> 在规则对象中设置基准测试之前,尝试填充benchmarkid并仅保存将保存behcmark对象的Rule。
@Override
public Rule saveRule(Rule rule) throws Exception {
try {
//Get BenchMark from rule...
// Your code to get Benchmark from rule.
// Populate benchmarkId
if (benchmark.getBenchmarkId() == null) {
benchmark.setBenchmarkId(getBenchmarkCount() + 1);
}
getSession().saveOrUpdate(rule);
getSession().flush();
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}
return rule;
}
PS:请参考Hibernate文档。