3

In Hibernate, there are options to manage hibernate and i have tried all most all the isolation levels on query along with locks but still i can see dirty read in my code.

So i have tried following things,

class A {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
int id;


...

//setter and getter
}

Another object

class B {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="bid")
int bid;

...
@ManyToOne(fetch = FetchType.EAGER )
@JoinColumn(name = "id")
private A a;

@Column(name="value")
int value
//setter and getter
}

class E {
//POJO having ManyToOne relation on class A + extra fields

//setter and getter
}

Now i have tried all 4 isolation level, REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED | SERIALIZABLE as specified in http://dev.mysql.com/doc/refman/5.7/en/set-transaction.html but still i can see dirty read on the table.

So how i am using this is,

class C {
@Autowired
private EntityManager entityManager;

public B getB(A a){
TypedQuery<B> query = entityManager.createNamedQuery("A.getB",B.class);
query.setParameter("a",a);
query.setLockMode(LockModeType.PESSIMISTIC_WRITE) //I have tried almost every type of lock here ranging from OPTIMISTIC_LOCK,OPTIMISTIC_FORCE_INCREMENT, PESSIMISTIC_READ, PESSIMISTIC_WRITE but even i can see dirty read
B b = query.getSingleResult();
return b;
}
}


class D {
@Autowired
private C c;
 @Transactional(isolation = Isolation.SERIALIZABLE)
private B getB(A a) {
//Multiple DB calls within transaction here.

B b = c.getB(a);
b.setValue(b+10);
b.merge();
}
}

So using Serializable, i can see it working but many calls are failed saying ROLLBACK DUE TO DEADLOCK, when there are concurrent calls.

But when i change isolation level or lockmode, its not working , i can see dirty read in this.

I checked locks using

SHOW OPEN TABLES  from DB_NAME

this gives me all the locks/threads waiting for locks. Can anyone help me to prevent from deadlock or from dirty read.

Naruto
  • 4,221
  • 1
  • 21
  • 32
  • So you have entity A referencing B and B referencing A and you are wondering why there are deadlocks? It smells, or I misunderstood. Can you share your DB model? – Betlista Dec 26 '15 at 20:55
  • 1
    I'd say, that this question will help you - http://stackoverflow.com/questions/11938253/jpa-joincolumn-vs-mappedby – Betlista Dec 26 '15 at 21:01
  • missed few points ...added it..it was not A refering to B and B refering to A. It is because using Serializable, lock was on table instead of row of mysql tabless, so one thread acquired lock on one table and another table acquired lock on another. I tried by decreasing isolation level, then there is data inconsistency.Can you please check. – Naruto Dec 27 '15 at 02:39
  • Are you really using class names as A, B, C, D, E ? It's difficult to find out what you want to achieve... Can you share more about what you want to achieve in transaction? `b.setValue(b+10);` doesn't really make sense - `b` as `B` and also integer? How are you testing using multiple threads? What is your spring configuration? – Betlista Dec 27 '15 at 09:18

3 Answers3

1

Dirty reads are only permitted in read_uncommitted, and if you use the default MySQL isolation level (repeatable_read), there's no chance you'll read uncommitted values.

Your code does not indicate that you are reading dirty values either, so I think you are using the wrong term here. It's normal to see deadlocks under serializable, that's the price you pay for having a stricter isolation level, and MySQL uses locks when using serializable.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
0

This is not really an answer, but too long for comment...

What should I do, to reproduce your problem?

I have spring configuration

<?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:tx="http://www.springframework.org/schema/tx"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/jee     http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/tx      http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        ">

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/so" />
        <property name="username" value="root" />
        <property name="password" value="" />
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="test" />
    </bean>

    <bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

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

    <!-- My beans -->
    <bean id="dao" class="test.Dao">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

</beans>

my entities are similar to yours

package test;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="a")
public class A {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

package test;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="b")
public class B {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="id")
    private int id;

    @ManyToOne
    @JoinColumn(name = "a_id")
    private A a;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
}

My DAO is:

package test;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
public class Dao {

    SessionFactory sessionFactory;

    @Transactional
    public void insertA() {
        A a = new A();
        sessionFactory.getCurrentSession().persist(a);
        System.out.println("a: a.id=" + a.getId());
    }

    @Transactional
    public void insertB() {
        A a = new A();
        sessionFactory.getCurrentSession().persist(a);
        System.out.println("b: a.id=" + a.getId());

        B b = new B();
        b.setA(a);
        sessionFactory.getCurrentSession().persist(b);
        System.out.println("b: b.id=" + b.getId());
    }

    @Required
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

}

and my "test" is:

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring.xml");
        Dao dao = ac.getBean(Dao.class);
        dao.insertA();
        dao.insertA();
        dao.insertB();
    }

}

and my maven dependencies are:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.2.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>4.2.4.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>5.0.6.Final</version>
    </dependency>

    <dependency>
        <groupId>javax.transaction</groupId>
        <artifactId>jta</artifactId>
        <version>1.1</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.38</version>
    </dependency>

</dependencies>
Betlista
  • 10,327
  • 13
  • 69
  • 110
0

Thanks Vlad. I actually used

entitymanager.refresh(object,LockModeType.PESSIMISTIC_WRITE)

which actually hepled me to get everytime the latest value from database with write lock hence solved the problem. It fetches the latest value from the Database and is analogy to select ... for update.

Naruto
  • 4,221
  • 1
  • 21
  • 32