データの更新
今回はレコード更新についてです。
【前提条件】
[環境]
- JDK 1.7.0_07
- Glassfish 3.1.2.2
- PostgreSQL 9.1(JDBC:postgresql-9.1-901.jdbc4.jar)
【概要】
EntityManagerによって管理されているオブジェクトに対するpersistは更新が行われ、
管理されていないオブジェクトに対するpersistは登録が行われます。
そのため、JPAでレコードの更新を行う場合、エンティティを取得する必要があります。
EntityManager#mergeメソッドを使うと
EntityManagerによって管理されていなくても更新できるようですが、
そもそもの用途として異なるようです。
非常に参考になるStackoverflowの議論。
JPA EntityManager: Why use persist() over merge()? - Stack Overflow
【サンプルコード】
[テーブル]
今回は以前作成した
「userdata」テーブルと「user_authority」テーブルを使います。
CREATE TABLE userdata ( user_id character varying(10) NOT NULL, name character varying(60), last_update timestamp with time zone NOT NULL DEFAULT now(), CONSTRAINT user_primary_key PRIMARY KEY (user_id ) )
CREATE TABLE user_authority ( seq_number serial NOT NULL, auth_user_id character varying(10) NOT NULL, authority_type integer, CONSTRAINT authority_primary_key PRIMARY KEY (seq_number ) )
[Entity]
エンティティのクラスです。
UserData,UserAuthorityともに以前と変更はありません。
package jp.glory.persistence.entity; import java.io.Serializable; import java.sql.Timestamp; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name = "userdata") @NamedQueries({ @NamedQuery(name = "findAll", query = "SELECT u FROM UserData u"), @NamedQuery(name = "findByUserId", query = "SELECT u FROM UserData u WHERE u.userId = :userId"), }) public class UserData implements Serializable { private static final long serialVersionUID = 6528642302239465812L; @Id @Column(name = "user_id") private String userId = null; private String name = null; @Column(name = "last_update") private Timestamp lastUpdate = null; @OneToMany(cascade = CascadeType.ALL) @JoinColumns({ @JoinColumn(name = "auth_user_id", referencedColumnName = "user_id") }) private List<UserAuthority> authorityList = null; // アクセサメソッドは省略 }
package jp.glory.persistence.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; @Entity @Table(name = "user_authority") @NamedQueries({ @NamedQuery(name = "deleteAuthorityByUserId", query = "DELETE FROM UserAuthority ua WHERE ua.authUserId = :userId") }) public class UserAuthority implements Serializable { private static final long serialVersionUID = 1625221760062551052L; @Id @Column(name = "seq_number") @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "user_authority_seq_number_seq") private int seqNumber = 0; @Column(name = "auth_user_id") private String authUserId = null; @Column(name = "authority_type") private int authorityType = 0; // アクセサメソッドは省略 }
[更新クラス]
更新を行うEJBのクラスです。
package jp.glory.application; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import jp.glory.persistence.entity.UserData; @Stateless public class UserEntryApplication { @PersistenceContext(unitName = "blog") private EntityManager manager = null; public void merge(final UserData userData) { mergeVersion2(userData); } private void mergeVersion1(final UserData userData) { final UserData updateData = manager.find(UserData.class, userData.getUserId()); if (updateData == null) { manager.persist(userData); } else { Query query = manager.createNamedQuery("deleteAuthorityByUserId"); query.setParameter("userId", userData.getUserId()); query.executeUpdate(); updateData.getAuthorityList().clear(); updateData.getAuthorityList().addAll(userData.getAuthorityList()); updateData.setName(userData.getName()); updateData.setLastUpdate(userData.getLastUpdate()); manager.persist(updateData); } } private void mergeVersion2(final UserData userData) { Query query = manager.createNamedQuery("deleteAuthorityByUserId"); query.setParameter("userId", userData.getUserId()); query.executeUpdate(); manager.merge(userData); } }
サンプルのmergeVersion1メソッドでは
persistメソッドを使用する場ので
一度、EntityManagerからエンティティのオブジェクトを取得しています。
サンプルのmergeVersion2メソッドでは
mergeメソッドを使用しているので、事前の取得を行っていません。
余談
サンプルコードを作ってから、
UserAuthorityエンティティの設計がいけてないなぁと反省したり・・・
削除フラグで制御すれば、事前のDELETEはいらなかったのではと思います・・・
[ManagedBean]
登録画面のManagedBeanです。
package jp.glory.ui; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import javax.ejb.EJB; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; import jp.glory.application.UserEntryApplication; import jp.glory.persistence.entity.UserAuthority; import jp.glory.persistence.entity.UserData; @ManagedBean(name = "userEntryPage") @RequestScoped public class UserEntryPage { @EJB private UserEntryApplication application = null; private String userId = null; private String name = null; private boolean authority1 = false; private boolean authority2 = false; private boolean authority3 = false; private boolean authority4 = false; public void entry() { UserData userData = createData(); application.merge(userData); clearInputData(); } private UserAuthority createAuthority(final int authorityType) { final UserAuthority authority = new UserAuthority(); authority.setAuthorityType(authorityType); authority.setAuthUserId(userId); return authority; } private void clearInputData() { userId = null; name = null; authority1 = false; authority2 = false; authority3 = false; authority4 = false; } private UserData createData() { final UserData userData = new UserData(); userData.setUserId(userId); userData.setName(name); userData.setLastUpdate(new Timestamp(System.currentTimeMillis())); final List<UserAuthority> authorityList = new ArrayList<>(); if (authority1) { authorityList.add(createAuthority(1)); } if (authority2) { authorityList.add(createAuthority(2)); } if (authority3) { authorityList.add(createAuthority(3)); } if (authority4) { authorityList.add(createAuthority(4)); } userData.setAuthorityList(authorityList); return userData; } // アクセサメソッドは省略 }
[ページ]
登録画面のページです。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"> <f:view> <h:head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>登録サンプル</title> </h:head> <h:body> <h:form id="entryForm"> <div> ユーザID:<h:inputText id="userId" value="#{userEntryPage.userId}" /> </div> <div> ユーザ名:<h:inputText id="name" value="#{userEntryPage.name}" /> </div> <div> 権限1:<h:selectBooleanCheckbox value="#{userEntryPage.authority1}" /> </div> <div> 権限2:<h:selectBooleanCheckbox value="#{userEntryPage.authority2}" /> </div> <div> 権限3:<h:selectBooleanCheckbox value="#{userEntryPage.authority3}" /> </div> <div> 権限4:<h:selectBooleanCheckbox value="#{userEntryPage.authority4}" /> </div> <div> <h:commandButton action="#{userEntryPage.entry()}" value="登録" /> </div> </h:form> </h:body> </f:view> </html>