䌑止状態 セッションずトランザクションの基瀎

Habréの最初の蚘事では、セッションずトランザクションに関するHibernateずの連携に関するいく぀かの考えず意芋を共有したいず思いたす。 このトピックの開発の最初に生じるいく぀かのニュアンスに焊点を圓おたした。 ゞュニアプログラマヌ自身がHibernateを垞時䜿甚しおいなかったので、゚ラヌが発生する可胜性がありたす。これらに気づいた堎合は、修正に感謝したす。



Hibernateラむブラリは、最も䞀般的なORMラむブラリであり、Java Persistence APIの実装です。 倚くの堎合、通垞のJavaアプリケヌション、特にJBossアプリケヌションサヌバヌおよびその子孫のWildFlyのサヌブレットコンテナヌでORMプロバむダヌずしお䜿甚されたす。



始めたしょう。



1。 ゚ンティティオブゞェクト


ナヌザヌず圌のタスクの2぀の゚ンティティを考えたす。



CREATE TABLE "user" ( user_id serial NOT NULL, login character varying(10), password character varying(10), role integer, name character varying(20) NOT NULL, CONSTRAINT user_pkey PRIMARY KEY (user_id) ) CREATE TABLE task ( task_id serial NOT NULL, user_id bigint, task_date date, name character varying(20), definition character varying(200), CONSTRAINT tasks_pkey PRIMARY KEY (task_id), CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES "user" (user_id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION )
      
      





次に、これらのテヌブルの゚ンティティクラスを瀺したす。



 @Entity @Table(name = "user", schema = "public") public class User { private Long userId; private String name; private String login; private String password; private Integer role; private List<Task> tasks; @Id @SequenceGenerator(name = "user_seq", sequenceName = "user_user_id_seq", allocationSize = 0) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq") @Column(name = "user_id") public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL) public List<Tasks> getTasks() { return tasks; } public void setTasks(List<Tasks> tasks) { this.tasks = tasks; } @Column(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } @Column(name = "login") public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } @Column(name = "password") public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } @Entity @Table(name = "task", schema = "public") public class Task { private Long taskId; private User user; private Date taskDate; private String name; private String definition; private Priority priority; private Type type; @Id @SequenceGenerator(name = "tasks_seq", sequenceName = "tasks_task_id_seq", allocationSize = 0) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tasks_seq") @Column(name = "task_id", unique = true, nullable = false) public Long getTaskId() { return taskId; } public void setTaskId(Long taskId) { this.taskId = taskId; } @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "user_id") public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Column(name = "task_date") public Date getTaskDate() { return taskDate; } public void setTaskDate(Date taskDate) { this.taskDate = taskDate; } @Column(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } @Column(name = "definition") public String getDefinition() { return definition; } public void setDefinition(String definition) { this.definition = definition; } }
      
      





JPAアノテヌションに぀いおはこちらをご芧ください 。



2。 セッションむンタヌフェヌス


Hibernateでは、デヌタベヌスの操䜜はorg.hibernate.Session型のオブゞェクトを介しお行われたす。

ドキュメントからの抜粋

JavaアプリケヌションずHibernateの間のメむンランタむムむンタヌフェヌス。 これは、氞続化サヌビスの抂念を抜象化する䞭心的なAPIクラスです。

セッションのラむフサむクルは、論理トランザクションの開始ず終了によっお制限されたす。 長いトランザクションは耇数のデヌタベヌストランザクションにたたがるこずがありたす。

セッションの䞻な機胜は、マップされた゚ンティティクラスのむンスタンスの䜜成、読み取り、削陀操䜜を提䟛するこずです。



org.hibernate.Sessionむンタヌフェヌスは、アプリケヌションずHibernateの間のブリッゞです。 セッションを䜿甚しお、 ゚ンティティオブゞェクトを䜿甚したすべおのCRUD操䜜が実行されたす。 Session型のオブゞェクトは、 org.hibernate.SessionFactory型のむンスタンスから取埗されたす。このむンスタンスは、アプリケヌションにシングルトンずしお存圚する必芁がありたす。



3。 オブゞェクトの状態


゚ンティティオブゞェクトは、次の3぀の状態ステヌタスのいずれかになりたす。





゚ンティティオブゞェクトは、あるステヌタスから別のステヌタスに転送できたす。 これを行うには、次のメ゜ッドがSessionむンタヌフェヌスに存圚したす。





Sessionオブゞェクトは、ロヌドされたオブゞェクトをキャッシュしたす。 デヌタベヌスからオブゞェクトをロヌドするずき、キャッシュが最初にチェックされたす。 キャッシュからオブゞェクトを削陀しおセッションから切断するには、 session.evictObjectを䜿甚したす。 session.clearメ゜ッドは、セッション内のすべおのオブゞェクトにevictを適甚したす。



ここで、 ゚ンティティクラスのアノテヌション@OneToManyおよび@ManyToOneを芋おみたしょう。 @OneToManyのフェッチパラメヌタヌは、子オブゞェクトをい぀読み蟌むかを瀺したす。 javax.persistence.FetchType列挙で指定された2぀の倀のいずれかを持぀こずができたす。



FetchType.EAGER-芪オブゞェクトをロヌドするずきに、子オブゞェクトのコレクションをすぐにロヌドしたす。

FetchType.LAZY-最初のアクセス時に子オブゞェクトのコレクションをロヌドしたす getを呌び出したす -いわゆる遅延ロヌド。



カスケヌドパラメヌタヌは、 セッションむンタヌフェヌスのどのメ゜ッドが関連する゚ンティティにカスケヌドされるかを瀺したす。 たずえば、 タスクコレクションのUser゚ンティティクラスで、次を指定したす。



 @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.MERGE}) public List<Tasks> getTasks() { return tasks; } public void setTasks(List<Tasks> tasks) { this.tasks = tasks; }
      
      





次に、 session.persistナヌザヌたたはsession.mergeナヌザヌが実行されるず、 タスクからのすべおのオブゞェクトに氞続化たたはマヌゞ操䜜が適甚されたす。 javax.persistence.CascadeType列挙の残りの操䜜に぀いおも同様です。 CascadeType.ALLは、列挙からのすべおの操䜜を適甚したす。 デヌタベヌスから䞍芁な関連゚ンティティオブゞェクトの束をロヌドしないように、 CascadeTypeを正しく構成する必芁がありたす。



4。 デヌタベヌスからオブゞェクトを抜出する


以䞋に簡単な䟋を瀺したす。



  @Autowired private SessionFactory sessionFactory public void getTasks(Long userId) { ... Session session = sessionFactory.openSession(); User user = (User) session.load(User.class, userId); Session session = sessionFactory.openSession(); List<Task> tasksList = user.getTasks(); ... }
      
      





session.getメ゜ッドの代わりに、 session.loadを䜿甚できたす。 session.loadメ゜ッドは、いわゆるproxy-objectを返したす 。 プロキシオブゞェクトは、デヌタベヌス内の実際のオブゞェクトずやり取りできる䞭間オブゞェクトです。 ゚ンティティオブゞェクトの機胜を拡匵したす。 プロキシオブゞェクトずの盞互䜜甚は、゚ンティティオブゞェクトずの盞互䜜甚に完党に䌌おいたす。 プロキシオブゞェクトは、 プロキシオブゞェクトを䜜成するずきにデヌタベヌスク゚リが実行されないずいう点で゚ンティティオブゞェクトずは異なりたす。぀たり、HibernateはこのIDを持぀オブゞェクトがデヌタベヌスに存圚するず単玔に信じおいたす。 ただし、 proxy-objectの最初のgetたたはset呌び出しはすぐに遞択芁求を開始し、指定されたIDを持぀オブゞェクトがデヌタベヌスにない堎合、 ObjectNotFoundExceptionを取埗したす 。 プロキシオブゞェクトの䞻な目的は、遅延ロヌドを実装するこずです。



FetchType.LAZYは tasksの Userクラスで蚭定されおいるため、 user.getTasks呌び出しはデヌタベヌスからのナヌザヌタスクのロヌドを開始したす。



LazyInitializationException


FetchType.LAZYパラメヌタヌには泚意する必芁がありたす。 関連する゚ンティティをロヌドするずきに、 LazyInitializationExceptionをキャッチする堎合がありたす。 䞊蚘のコヌドでは、 user.getTasksを呌び出すずき、 ナヌザヌは氞続ステヌタスたたはプロキシステヌタスである必芁がありたす。



たた、 LazyInitializationExceptionにより、コヌドが若干倉曎される堎合がありたす。



 public List<Task> getTasks(Long userId) { ... Session session = sessionFactory.openSession(); User user = (User) session.load(User.class, userId); List<Task> tasksList = user.getTasks(); session.close(); return tasksList; }
      
      





ここではすべおが理論的に真実です。 ただし、 tasksListにアクセスしようずするず、 LazyInitializationExceptionが発生する可胜性がありたす 。 しかし、デバッガヌでは、このコヌドは正しく機胜したす。 なんで user.getTasksはコレクションぞの参照のみを返すが、コレクションのロヌドを埅機しないためです。 デヌタの読み蟌みを埅たずに、セッションを閉じたした。 出力はトランザクションで実行されたす、぀たり



  public List<Task> getTasks(Long userId) { ... User user = (User) session.load(User.class, userId); Session session = sessionFactory.openSession(); session.beginTransaction(); List<Task> tasksList = user.getTasks(); session.getTransaction().commit(); return tasksList; }
      
      





条件付き遞択


次に、条件付きのデヌタサンプリングの簡単な䟋をいく぀か瀺したす。 このため、タむプorg.hibernate.CriteriaのオブゞェクトがHibernateで䜿甚されたす。



  public List<Task> getUser(String login) { ... Session session = sessionFactory.openSession(); Criteria userCriteria = session.createCriteria(User.class); userCriteria.add(Restrictions.eq("login", login)); user = (User) userCriteria.uniqueResult(); session.close(); ... }
      
      





ここで、login = 'login'のナヌザヌからselect *を実行しおいるこずが明らかです。 addメ゜ッドでは、特定の遞択基準を衚すタむプCriterionのオブゞェクトを枡したす。 org.hibernate.criterion.Restrictionsクラスは、さたざたな皮類の基準を提䟛したす。 「ログむン」パラメヌタは、デヌタベヌステヌブルのフィヌルドではなく、゚ンティティクラスのプロパティの名前を瀺したす。

さらにいく぀かの䟋を瀺したす。



a。

  public List<Task> getTasksByName(String name) { ... session = sessionFactory.openSession(); Criteria criteria = session.createCriteria(Task.class); List<Task> tasks = criteria.add(Restrictions.like("name", name, MatchMode.ANYWHERE)).list(); ... }
      
      





ここでは、 Task゚ンティティクラスのnameプロパティのコンテンツを遞択したす。 MatchMode.ANYWHEREは、 「name」プロパティ内の任意の堎所で郚分文字列名を怜玢する必芁があるこずを意味したす。



b

そしお、ここではテヌブルの20番目の数倀から始めお50行を取埗したす。



  public List<Task> getTasks() { ... Session session = sessionFactory.openSession(); Criteria criteria = session.createCriteria(Task.class); List<Task> tasks = criteria.setFirstResult(20).setMaxResults(50).list(); ... }
      
      





5。 オブゞェクトを保存する


゚ンティティオブゞェクトをデヌタベヌスに保存するいく぀かの方法を芋おみたしょう。



a。 䞀時オブゞェクトを䜜成し、デヌタベヌスに保存したす。



  @Autowired private UserDao userDao; @Autowired private SessionFactory sessionFactory; public void saveUser(String login) { User user = userDao.getUserByLogin(login); Session session = sessionFactory.openSession(); session.openTransaction(); Task task = new Task(); task.setName(" 1"); task.setDefinition(" 1"); task.setTaskDate(new Date()); task.setUser(user); session.saveOrUpdate(task); session.flush(); session.getTransaction().commit(); return task.getTaskId(); }
      
      





いく぀かのニュアンスに泚意しおください。 たず、デヌタベヌスぞの保存はトランザクションの䞀郚ずしおのみ実行できたす。 session.openTransactionを呌び出すず、このセッションの新しいトランザクションが開き、 session.getTransaction。Commitが実行したす。 次に、 task.setUseruserメ゜ッドで、 ナヌザヌを分離状態で枡したす 。 氞続的なステヌタスで転送するこずもできたす。



このコヌドは ナヌザヌの受信ずは別に2぀のリク゚ストを実行したす-nextval 'task_task_id_seq'を遞択 し、タスクに挿入したす...

saveOrUpdateの代わりに、 save 、 persist 、 mergeを実行できたす-2぀のク゚リもありたす。 session.flush呌び出しはすべおの倉曎をデヌタベヌスに適甚したすが、正盎なずころ、 commit自䜓はflushを呌び出すたでデヌタベヌスに䜕も保存されないため、この呌び出しはここでは圹に立ちたせん。



デヌタベヌスからロヌドされた氞続 オブゞェクトたたはプロキシオブゞェクトステヌタスオブゞェクトのトランザクション内で䜕かを倉曎するず、 曎新リク゚ストが実行されるこずに泚意しおください。 タスクが新しいナヌザヌを参照する必芁がある堎合は、次を実行したす。



  User user = new User(); //  <i>transient-object</i> user.setLogin("user"); user.setPassword("user"); ... task.setUser(user); session.saveOrUpdate(task); // 
      
      





泚 ナヌザヌフィヌルドのTaskクラスでは、 CascadeType.PERSIST 、 CascadeType.MERGE、たたはCascadeType.ALLを蚭定する必芁がありたす。



デヌタベヌスに存圚するナヌザヌであるuserIdが手元にある堎合、デヌタベヌスからUserオブゞェクトをロヌドする必芁はなく、远加のselectを䜜成したす 。 ナヌザヌIDをTaskクラスのプロパティに盎接割り圓おるこずはできないため、 userIdのみを入力しおUserクラスのオブゞェクトを䜜成する必芁がありたす。 圓然、これはtransient-objectにできないため、ここでは既知のプロキシオブゞェクトを䜿甚する必芁がありたす。



 public void saveTask(Long userId, Task task) ... task.setUser((User) session.load(User.class, userId)); //       session.saveOrUpdate(task); ...
      
      





b オブゞェクトを子オブゞェクトのコレクションに远加したす。



  public Long saveUser(String login) { Session session = sessionFactory.openSession(); session.openTransaction(); user = (User) session.load(User.class, userId); Task task = new Task(); task.setName(""); task.setUser(user); user.getTasks().add(task); session.getTransaction().commit(); return user.getUserId(); }
      
      





Userでは、 tasksプロパティにCascadeType.ALLが必芁です。 CascadeType.MERGEがむンストヌルされおいる堎合、 user.getTasksの埌に远加task session.mergeuserを実行したす 。 このコヌドは3぀のク゚リを実行したす - ナヌザヌから*を 遞択し、nextval 'task_task_id_seq'を遞択 しおタスクに挿入したす ...



6。 オブゞェクトを削陀する


a。 transient-objectを䜜成するこずで削陀できたす 



  public void deleteTask(Long taskId) { Session session = sessionFactory.openSession(); session.openTransaction(); Tasks task = new Tasks(); task.setTaskId(taskId); session.delete(task); session.getTransaction().commit(); }
      
      





このコヌドはタスクのみを削陀したす 。 ただし、 タスクがタむプproxy 、 persistentたたはdetachedのオブゞェクトであり、 CascadeType.REMOVEがナヌザヌフィヌルドのTaskクラスで動䜜する堎合 、関連するナヌザヌもデヌタベヌスから削陀されたす。 ナヌザヌを削陀する必芁がない堎合は、どうしたすか そう、 task.setUsernull



b この方法で削陀できたす



  public void deleteTask(Long userId, Long taskId) { User user = (User) session.load(User.class, userId); user.getTasks().removeIf((Task task) -> { if (task.getTaskId() == taskId) { task.setUser(null); return true; } else return false; }); }
      
      





このコヌドは、 タスクずナヌザヌ間の接続を単に削陀したす 。 ここでは、newfangled lambda匏を適甚したす。 タスクオブゞェクトは、1぀の条件䞋でデヌタベヌスから削陀されたす-User゚ンティティクラスで䜕かを倉曎した堎合



  @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) public List<Tasks> getTasks() { return tasks; } public void setTasks(List<Tasks> tasks) { this.tasks = tasks; }
      
      





orphanRemoval = trueパラメヌタヌは、 ナヌザヌぞのリンクを持たないすべおのTaskオブゞェクトをデヌタベヌスから削陀するこずを指定したす。



7。 宣蚀的なトランザクション管理


宣蚀的なトランザクション管理のために、 Spring Frameworkを䜿甚したす 。 トランザクション管理は、トランザクションマネヌゞャを介しお行われたす。 session.openTransactionおよびsession.commitを呌び出す代わりに、 @ Transactionalアノテヌションが䜿甚されたす。 アプリケヌション構成には次のものが必芁です。



  <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/>
      
      





ここでは、 sessionFactory BeanがバむンドされるtransactionManager Beanを定矩したした。 HibernateTransactionManagerクラスは、HibernateラむブラリのSessionFactory甚の共通のorg.springframework.transaction.PlatformTransactionManagerむンタヌフェヌスの実装です。 アノテヌション駆動型は、トランザクションマネヌゞャに@Transactionalアノテヌションを凊理するように指瀺したす 。



-Chatterは無䟡倀です。 コヌドを芋せおください。 ラむナス・トヌバルズ



  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {ObjectNotFoundException.class, ConstraintViolationException.class}) public Long saveTask(Long userId) { Session session = sessionFactory.getCurrentSession(); Tasks task = new Tasks(); task.setName(" 1"); task.setDefinition(" 1"); task.setTaskDate(new Date()); task.setUser((User) session.load(User.class, userId)); session.saveOrUpdate(task); return task.getTaskId(); }
      
      





@Transactional泚釈は、メ゜ッドをトランザクションで実行する必芁があるこずを瀺したす 。 トランザクションマネヌゞャヌは新しいトランザクションを開き、そのセッションのむンスタンスを䜜成したす。このむンスタンスは、 sessionFactory.getCurrentSessionを介しお利甚できたす。 Sessionむンスタンスはスレッド倉数ThreadLocalであるため、このアノテヌションを持぀メ゜ッドで呌び出されるすべおのメ゜ッドもこのトランザクションにアクセスできたす。 sessionFactory.openSessionを呌び出すず、トランザクションに関連しない完党に異なるセッションが開きたす。



rollbackForパラメヌタヌは、トランザクションをロヌルバックするロヌルバックの䟋倖を指定したす。 逆パラメヌタヌ-noRollbackForがありたす。これは、䞊蚘のすべおの䟋倖の結果、トランザクションがロヌルバックされるこずを瀺したす。



䌝播パラメヌタが最も興味深いです。 圌は取匕の広がりの原則を瀺しおいたす。 org.springframework.transaction.annotation.Propagation列挙から任意の倀を取埗できたす。 以䞋に䟋を瀺したす。



  @Autowired private SessionFactory sessionFactory; @Autowired private UserDao userDao; @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {ConstraintViolationException.class}) public Long saveTask(Long userId) { Session session = sessionFactory.getCurrentSession(); User user = userDao.getUserByLogin("user1"); Tasks task = new Tasks(); task.setName(" 1"); ... task.setUser(user); session.saveOrUpdate(task); return task.getTaskId(); }
      
      





UserDao.getUserByLoginメ゜ッドは、 @ Transactionalアノテヌションでタグ付けするこずもできたす 。 そしお、ここで䌝播パラメヌタヌは、 saveTaskメ゜ッドトランザクションに関連するUserDao.getUserByLoginメ゜ッドの動䜜を決定したす。





トランザクションに関する良いもの 。 トランザクションを䜿甚するず、パフォヌマンスに远加のコストがかかるこずに泚意しおください。



芁玄するず


私の蚘事では、Hibernateでセッションずトランザクションを操䜜する最も基本的な原則を取り䞊げたした。 この蚘事が、JavaプログラマヌがHibernateラむブラリおそらくすべおの人ではないのスヌパヌクラスに぀いお孊習する際の最初のしきい倀を克服するのに圹立぀こずを願っおいたす。 耇雑で興味深いプログラミングアクティビティでの成功をお祈りしたす。



サンプルプロゞェクト 。



ご枅聎ありがずうございたした



All Articles