Home > スポンサー広告 > Hibernateで変更されたカラムのみをUpdate

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Comments:-

Comment Form

Trackback+Pingback:-

TrackBack URL for this entry
http://netforestdevnote.blog26.fc2.com/tb.php/32-ee74b9c7
Listed below are links to weblogs that reference
スポンサーサイト from Netforest Developer's Note

Home > スポンサー広告 > Hibernateで変更されたカラムのみをUpdate

Home > Hibernate > Hibernateで変更されたカラムのみをUpdate

Hibernateで変更されたカラムのみをUpdate

take©です。
Session#saveOrUpdate()は、1つのカラムのみの変更でも全てのカラムに対してUpdateするSQLを発行します。 今回は、HibernateのSession#merge()を使って変更されたカラムのみUpdateする方法をメモします。

エンティティを用意

sample/Person.java

package sample;

import java.util.Date;

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

@Entity
@org.hibernate.annotations.Entity(dynamicInsert=true,dynamicUpdate=true)
public class Person {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	
	private String name;
	
	private int age;
	
	@Version
	private Date timestamp;

	public int getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Date getTimestamp() {
		return timestamp;
	}

	public void setTimestamp(Date timestamp) {
		this.timestamp = timestamp;
	}
}

@org.hibernate.annotations.Entity(dynamicInsert=true,dynamicUpdate=true)
dynamicInsert=trueは、Insert時にnullカラムを除いたSQLを発行してくれます。
dynamicUpdate=trueは、Update時に変更されたカラムのみを更新するSQLを発行してくれます。
※org.hibernate.annotations.Entityはjavax.persistence.Entityの代わりではなく、両方必要なので注意!

@Version
dynamicUpdateを指定した場合、楽観的ロックが利用できます。 今回は日付をversionカラムとして利用します。 永続化対象をSession#merge()する前に、Session#lock()をすることで、Hibernateがすでに更新されているかどうかを日付を比較して確認してくれます。
dynamicUpdateを使用する場合、versionカラムを利用することがパフォーマンス上推奨されます。

Hibernateの設定ファイルを用意

sample/hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
	<session-factory>
		<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
		<property name="connection.driver_class">org.postgresql.Driver</property>
		<property name="connection.username">[username]</property>
		<property name="connection.password">[password]</property>
		<property name="connection.url">jdbc:postgresql://[hostname]:[post]/[dbname]</property>

		<property name="current_session_context_class">thread</property>
		<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

		<property name="show_sql">true</property>
		<property name="hibernate.format_sql">true</property>
		<property name="hibernate.use_sql_comments">true</property>

		<property name="hbm2ddl.auto">update</property>

		<mapping package="sample"/>
		<mapping class="sample.Person"/>
	</session-factory>
</hibernate-configuration>

今回は、PostgreSQLを使ってみます。

実行クラスを用意

Main.java

import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

import sample.Person;

public class Main {
	public static void main(String[] args)
	throws Exception {
		SessionFactory factory = new AnnotationConfiguration()
			.configure("/sample/hibernate.cfg.xml")
			.buildSessionFactory();
		
		Session session = null;
		session = factory.openSession();
		session.beginTransaction();
		Person person = (Person) session.load(Person.class, 1);
		session.getTransaction().commit();
		session.close(); // personをSessionから切り離します
		
		session = factory.openSession();
		session.beginTransaction();
		session.lock(person, LockMode.UPGRADE);
		person.setName("太郎");
		session.merge(person);
		session.getTransaction().commit();
		factory.close();
	}
}

Webアプリでの利用を踏まえて、永続化対象がSessionから切り離された状態を再現しています。
personテーブルにidが「1」のデータが既に存在することが前提です。
ここで使用するのはもちろんSession#merge()です。事前にSession#lock()をしておきます。

実行!

用意したMain.javaを実行したときに発行されたSQL

Hibernate: 
    /* load sample.Person */ select
        person0_.id as id0_0_,
        person0_.age as age0_0_,
        person0_.name as name0_0_,
        person0_.timestamp as timestamp0_0_ 
    from
        Person person0_ 
    where
        person0_.id=?
Hibernate: 
    /* UPGRADE lock sample.Person */ select
        id 
    from
        Person 
    where
        id =? 
        and timestamp =? for update
            
Hibernate: 
    /* update
        sample.Person */ update
            Person 
        set
            name=?,
            timestamp=? 
        where
            id=? 
            and timestamp=?

1回目のSQL:ID=1のPersonを取得するSELECT文
2回目のSQL:永続化対象のtimestampが最新かどうかを確認することを兼ねた、SELECT FOR UPDATE文
3回目のSQL:nameとtimestampのUPDATE文

ageカラムは更新対象のカラムから外されていることが確認できました。


まとめ

Session#merge() & dynamicUpdateは、ORMのパフォーマンス戦略に欠かせない方法です。 JavaEE準拠にこだわらなければ、積極的に使っていっても良さそうです。

Comments:0

Comment Form

Trackback+Pingback:0

TrackBack URL for this entry
http://netforestdevnote.blog26.fc2.com/tb.php/32-ee74b9c7
Listed below are links to weblogs that reference
Hibernateで変更されたカラムのみをUpdate from Netforest Developer's Note

Home > Hibernate > Hibernateで変更されたカラムのみをUpdate

Recent Comments
Recent Trackback
Search
Meta
Links
Feeds

Page Top

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。