| /* |
| * Copyright (c) 2005, 2021 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2005, 2015 SAP. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // SAP - initial API and implementation |
| |
| package org.eclipse.persistence.testing.tests.wdf.jpa1.entitymanager; |
| |
| import java.sql.Connection; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| |
| import jakarta.persistence.EntityManager; |
| |
| import org.eclipse.persistence.testing.framework.wdf.JPAEnvironment; |
| import org.eclipse.persistence.testing.framework.wdf.ToBeInvestigated; |
| import org.eclipse.persistence.testing.models.wdf.jpa1.node.CascadingNode; |
| import org.eclipse.persistence.testing.models.wdf.jpa1.node.CascadingNodeDescription; |
| import org.eclipse.persistence.testing.tests.wdf.jpa1.JPA1Base; |
| import org.junit.Test; |
| |
| /* |
| * A managed entity instance becomes removed by invoking the remove method on it or by cascading the remove operation. The |
| * semantics of the remove operation, applied to an entity X are as follows: |
| * <ul> |
| * <li>If X is a new entity, it is ignored by the remove operation. However, the remove operation is cascaded to entities |
| * referenced by X, if the relationships from X to these other entities is annotated with the cascade=REMOVE or cascade=ALL |
| * annotation element value.</li> |
| * <li>If X is a managed entity, the remove operation causes it to become removed. The remove operation is cascaded to entities |
| * referenced by X, if the relationships from X to these other entities is annotated with the cascade=REMOVE or cascade=ALL |
| * annotation element value.</li> |
| * <li>If X is a detached entity, an IllegalArgumentException will be thrown by the remove operation (or the transaction commit |
| * will fail).</li> |
| * <li>If X is a removed entity, it is ignored by the remove operation.</li> |
| * <li>A removed entity X will be removed from the database at or before transaction commit or as a result of the flush |
| * operation.</li> |
| * </ul> |
| * After an entity has been removed, its state (except for generated state) will be that of the entity at the point at which the |
| * remove operation was called. |
| */ |
| public class TestCascadeRemove extends JPA1Base { |
| |
| @Test |
| public void testSimpleCascadeNew() throws SQLException { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| env.evictAll(em); |
| try { |
| // one-to-many relationship |
| CascadingNode child = new CascadingNode(1, null); |
| env.beginTransaction(em); |
| em.persist(child); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| child = em.find(CascadingNode.class, child.getId()); |
| CascadingNode parent = new CascadingNode(2, null); |
| parent.addChild(child); |
| verify(!em.contains(parent), "Parent not a new entity"); |
| verify(em.contains(child), "Child not managed"); |
| em.remove(parent); |
| env.commitTransactionAndClear(em); |
| verifyAbsenceFromNodeTable(parent.getId()); |
| verifyAbsenceFromNodeTable(child.getId()); |
| // one-to-one relationship |
| CascadingNodeDescription description = new CascadingNodeDescription(3, null, "a simple node"); |
| env.beginTransaction(em); |
| em.persist(description); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| description = em.find(CascadingNodeDescription.class, description.getId()); |
| parent = new CascadingNode(4, null); |
| parent.setDescription(description); |
| description.setNode(parent); |
| verify(!em.contains(parent), "Parent not a new entity"); |
| verify(em.contains(description), "Description not managed"); |
| em.remove(parent); |
| env.commitTransactionAndClear(em); |
| verifyAbsenceFromNodeTable(parent.getId()); |
| verifyAbsenceFromDescriptionTable(description.getId()); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void testSimpleCascadeManaged() throws SQLException { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| try { |
| // Case 1: status FOR_INSERT |
| // one-to-many relationship |
| CascadingNode child = new CascadingNode(101, null); |
| env.beginTransaction(em); |
| em.persist(child); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| CascadingNode parent = new CascadingNode(102, null); |
| em.persist(parent); |
| child = em.find(CascadingNode.class, child.getId()); |
| parent.addChild(child); |
| verify(em.contains(parent), "Parent not managed"); |
| verify(em.contains(child), "Child not managed"); |
| em.remove(parent); |
| env.commitTransactionAndClear(em); |
| verifyAbsenceFromNodeTable(parent.getId()); |
| verifyAbsenceFromNodeTable(child.getId()); |
| // one-to-one relationship |
| CascadingNodeDescription description = new CascadingNodeDescription(103, null, "a simple node"); |
| env.beginTransaction(em); |
| em.persist(description); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| parent = new CascadingNode(104, null); |
| em.persist(parent); |
| description = em.find(CascadingNodeDescription.class, description.getId()); |
| parent.setDescription(description); |
| description.setNode(parent); |
| verify(em.contains(parent), "Parent not managed"); |
| verify(em.contains(description), "Description not managed"); |
| em.remove(parent); |
| env.commitTransactionAndClear(em); |
| verifyAbsenceFromNodeTable(parent.getId()); |
| verifyAbsenceFromDescriptionTable(description.getId()); |
| // Case 2: status FOR_UPDATE |
| // one-to-many relationship |
| parent = new CascadingNode(105, null); |
| child = new CascadingNode(106, null); |
| env.beginTransaction(em); |
| em.persist(parent); |
| em.persist(child); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| parent = em.find(CascadingNode.class, parent.getId()); |
| child = em.find(CascadingNode.class, child.getId()); |
| parent.addChild(child); |
| verify(em.contains(parent), "Parent not managed"); |
| verify(em.contains(child), "Child not managed"); |
| em.remove(parent); |
| env.commitTransactionAndClear(em); |
| verifyAbsenceFromNodeTable(parent.getId()); |
| verifyAbsenceFromNodeTable(child.getId()); |
| // one-to-one relationship |
| parent = new CascadingNode(107, null); |
| description = new CascadingNodeDescription(108, parent, "a simple node"); |
| parent.setDescription(description); |
| env.beginTransaction(em); |
| em.persist(parent); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| parent = em.find(CascadingNode.class, parent.getId()); |
| description = parent.getDescription(); |
| verify(em.contains(parent), "Parent not managed"); |
| verify(em.contains(description), "Description not managed"); |
| em.remove(parent); |
| env.commitTransactionAndClear(em); |
| verifyAbsenceFromNodeTable(parent.getId()); |
| verifyAbsenceFromDescriptionTable(description.getId()); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void testSimpleCascadeDetached() throws SQLException { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| try { |
| // Case 1: detached because entity exists on db but is not known by persistence context |
| // one-to-many relationship |
| CascadingNode parent = new CascadingNode(201, null); |
| CascadingNode child = new CascadingNode(202, null); |
| env.beginTransaction(em); |
| em.persist(parent); |
| em.persist(child); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| child = em.find(CascadingNode.class, child.getId()); |
| parent.addChild(child); |
| verify(!em.contains(parent), "Parent not detached"); |
| verify(em.contains(child), "Child not managed"); |
| boolean removeFailed = false; |
| boolean immediateException = false; |
| try { |
| em.remove(parent); |
| verify(!em.contains(parent), "Parent is managed"); |
| verify(!em.contains(child), "Child is still managed"); |
| } catch (IllegalArgumentException e) { |
| removeFailed = true; |
| immediateException = true; |
| } |
| if (!immediateException) { |
| try { |
| env.commitTransactionAndClear(em); |
| } catch (RuntimeException e) { |
| if (!checkForPersistenceException(e)) { |
| throw e; |
| } |
| removeFailed = true; |
| } |
| } else { |
| env.rollbackTransactionAndClear(em); |
| } |
| verify(removeFailed, "remove succeeded on a detached instance"); |
| verifyExistenceInNodeTable(parent.getId()); |
| verifyExistenceInNodeTable(child.getId()); |
| // one-to-one relationship |
| parent = new CascadingNode(203, null); |
| CascadingNodeDescription description = new CascadingNodeDescription(204, null, "a simple node"); |
| env.beginTransaction(em); |
| em.persist(parent); |
| em.persist(description); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| description = em.find(CascadingNodeDescription.class, description.getId()); |
| parent.setDescription(description); |
| verify(!em.contains(parent), "Parent not detached"); |
| verify(em.contains(description), "Description not managed"); |
| removeFailed = false; |
| immediateException = false; |
| try { |
| em.remove(parent); |
| verify(!em.contains(parent), "Parent is managed"); |
| verify(!em.contains(description), "Description is still managed"); |
| } catch (IllegalArgumentException e) { |
| removeFailed = true; |
| immediateException = true; |
| } |
| if (!immediateException) { |
| try { |
| env.commitTransactionAndClear(em); |
| } catch (RuntimeException e) { |
| if (!checkForPersistenceException(e)) { |
| throw e; |
| } |
| removeFailed = true; |
| } |
| } else { |
| env.rollbackTransactionAndClear(em); |
| } |
| verify(removeFailed, "remove succeeded on a detached instance"); |
| verifyExistenceInNodeTable(parent.getId()); |
| verifyExistenceInDescriptionTable(description.getId()); |
| // Case 2: detached because an object with same pk but different object identity is known by persistence context |
| // Case 2a: state of known object: FOR_INSERT |
| // one-to-many relationship |
| CascadingNode existing = new CascadingNode(205, null); |
| parent = new CascadingNode(existing.getId(), null); |
| child = new CascadingNode(206, null); |
| env.beginTransaction(em); |
| em.persist(child); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| em.persist(existing); // status FOR_INSERT |
| child = em.find(CascadingNode.class, child.getId()); |
| parent.addChild(child); |
| verify(!em.contains(parent), "Parent not detached"); |
| verify(em.contains(existing), "Existing not managed"); |
| verify(em.contains(child), "Child not managed"); |
| removeFailed = false; |
| immediateException = false; |
| try { |
| em.remove(parent); |
| verify(!em.contains(parent), "Parent is managed"); |
| verify(!em.contains(child), "Child is still managed"); |
| } catch (IllegalArgumentException e) { |
| removeFailed = true; |
| immediateException = true; |
| } |
| verify(em.contains(existing), "Previously managed entity no longer managed"); |
| if (!immediateException) { |
| try { |
| env.commitTransactionAndClear(em); |
| } catch (RuntimeException e) { |
| if (immediateException || !checkForPersistenceException(e)) { |
| throw e; |
| } |
| removeFailed = true; |
| } |
| } else { |
| env.rollbackTransactionAndClear(em); |
| } |
| verify(removeFailed, "remove succeeded on a detached instance"); |
| verifyAbsenceFromNodeTable(existing.getId()); |
| verifyExistenceInNodeTable(child.getId()); |
| // one-to-one relationship |
| existing = new CascadingNode(207, null); |
| parent = new CascadingNode(existing.getId(), null); |
| description = new CascadingNodeDescription(208, null, "some text"); |
| env.beginTransaction(em); |
| em.persist(description); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| em.persist(existing); // status FOR_INSERT |
| description = em.find(CascadingNodeDescription.class, description.getId()); |
| parent.setDescription(description); |
| verify(!em.contains(parent), "Parent not detached"); |
| verify(em.contains(existing), "Existing not managed"); |
| verify(em.contains(description), "Description not managed"); |
| removeFailed = false; |
| immediateException = false; |
| try { |
| em.remove(parent); |
| verify(!em.contains(parent), "Parent is managed"); |
| verify(!em.contains(description), "Description is still managed"); |
| } catch (IllegalArgumentException e) { |
| removeFailed = true; |
| immediateException = true; |
| } |
| verify(em.contains(existing), "Previously managed entity no longer managed"); |
| if (!immediateException) { |
| try { |
| env.commitTransactionAndClear(em); |
| } catch (RuntimeException e) { |
| if (immediateException || !checkForPersistenceException(e)) { |
| throw e; |
| } |
| removeFailed = true; |
| } |
| } else { |
| env.rollbackTransactionAndClear(em); |
| } |
| verify(removeFailed, "remove succeeded on a detached instance"); |
| verifyAbsenceFromNodeTable(existing.getId()); |
| verifyExistenceInNodeTable(child.getId()); |
| // Case 2b: state of known object: FOR_UPADTE |
| // one-to-many relationship |
| existing = new CascadingNode(209, null); |
| parent = new CascadingNode(existing.getId(), null); |
| child = new CascadingNode(210, null); |
| env.beginTransaction(em); |
| em.persist(existing); |
| em.persist(child); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| existing = em.find(CascadingNode.class, existing.getId()); // state FOR_UPADTE |
| child = em.find(CascadingNode.class, child.getId()); |
| parent.addChild(child); |
| verify(!em.contains(parent), "Parent not detached"); |
| verify(em.contains(existing), "Existing not managed"); |
| verify(em.contains(child), "Child not managed"); |
| removeFailed = false; |
| immediateException = false; |
| try { |
| em.remove(parent); |
| verify(!em.contains(parent), "Parent is managed"); |
| verify(!em.contains(child), "Child is still managed"); |
| } catch (IllegalArgumentException e) { |
| removeFailed = true; |
| immediateException = true; |
| } |
| verify(em.contains(existing), "Previously managed entity no longer managed"); |
| if (!immediateException) { |
| try { |
| env.commitTransactionAndClear(em); |
| } catch (RuntimeException e) { |
| if (!checkForPersistenceException(e)) { |
| throw e; |
| } |
| removeFailed = true; |
| } |
| } else { |
| env.rollbackTransactionAndClear(em); |
| } |
| verify(removeFailed, "remove succeeded on a detached instance"); |
| verifyExistenceInNodeTable(existing.getId()); |
| verifyExistenceInNodeTable(child.getId()); |
| // one-to-one relationship |
| existing = new CascadingNode(211, null); |
| parent = new CascadingNode(existing.getId(), null); |
| description = new CascadingNodeDescription(212, null, "some text"); |
| env.beginTransaction(em); |
| em.persist(existing); |
| em.persist(description); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| existing = em.find(CascadingNode.class, existing.getId()); // state FOR_UPADTE |
| description = em.find(CascadingNodeDescription.class, description.getId()); |
| parent.setDescription(description); |
| verify(!em.contains(parent), "Parent not detached"); |
| verify(em.contains(existing), "Existing not managed"); |
| verify(em.contains(description), "Description not managed"); |
| removeFailed = false; |
| immediateException = false; |
| try { |
| em.remove(parent); |
| verify(!em.contains(parent), "Parent is managed"); |
| verify(!em.contains(description), "Description is still managed"); |
| } catch (IllegalArgumentException e) { |
| removeFailed = true; |
| immediateException = true; |
| } |
| verify(em.contains(existing), "Previously managed entity no longer managed"); |
| if (!immediateException) { |
| try { |
| env.commitTransactionAndClear(em); |
| } catch (RuntimeException e) { |
| if (!checkForPersistenceException(e)) { |
| throw e; |
| } |
| removeFailed = true; |
| } |
| } else { |
| env.rollbackTransactionAndClear(em); |
| } |
| verify(removeFailed, "remove succeeded on a detached instance"); |
| verifyExistenceInNodeTable(existing.getId()); |
| verifyExistenceInDescriptionTable(description.getId()); |
| // Case 2c: state of known object: FOR_REMOVE |
| // one-to-many relationship |
| existing = new CascadingNode(213, null); |
| parent = new CascadingNode(existing.getId(), null); |
| child = new CascadingNode(214, null); |
| env.beginTransaction(em); |
| em.persist(existing); |
| em.persist(child); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| existing = em.find(CascadingNode.class, existing.getId()); |
| em.remove(existing); // state FOR_REMOVE |
| child = em.find(CascadingNode.class, child.getId()); |
| parent.addChild(child); |
| verify(!em.contains(parent), "Parent not detached"); |
| verify(!em.contains(existing), "Existing not removed"); |
| verify(em.contains(child), "Child not managed"); |
| removeFailed = false; |
| immediateException = false; |
| try { |
| em.remove(parent); |
| verify(!em.contains(parent), "Parent is managed"); |
| verify(!em.contains(child), "Child is still managed"); |
| } catch (IllegalArgumentException e) { |
| removeFailed = true; |
| immediateException = true; |
| } |
| if (!immediateException) { |
| try { |
| env.commitTransactionAndClear(em); |
| } catch (RuntimeException e) { |
| if (!checkForPersistenceException(e)) { |
| throw e; |
| } |
| removeFailed = true; |
| } |
| } else { |
| env.rollbackTransactionAndClear(em); |
| } |
| verify(removeFailed, "remove succeeded on a detached instance"); |
| verifyExistenceInNodeTable(existing.getId()); |
| verifyExistenceInNodeTable(child.getId()); |
| // one-to-one relationship |
| existing = new CascadingNode(215, null); |
| parent = new CascadingNode(existing.getId(), null); |
| description = new CascadingNodeDescription(216, null, "some text"); |
| env.beginTransaction(em); |
| em.persist(existing); |
| em.persist(description); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| existing = em.find(CascadingNode.class, existing.getId()); |
| em.remove(existing); // state FOR_REMOVE |
| description = em.find(CascadingNodeDescription.class, description.getId()); |
| parent.setDescription(description); |
| verify(!em.contains(parent), "Parent not detached"); |
| verify(!em.contains(existing), "Existing not removed"); |
| verify(em.contains(description), "Description not managed"); |
| removeFailed = false; |
| immediateException = false; |
| try { |
| em.remove(parent); |
| verify(!em.contains(parent), "Parent is managed"); |
| verify(!em.contains(description), "Description is still managed"); |
| } catch (IllegalArgumentException e) { |
| removeFailed = true; |
| immediateException = true; |
| } |
| if (!immediateException) { |
| try { |
| env.commitTransactionAndClear(em); |
| } catch (RuntimeException e) { |
| if (!checkForPersistenceException(e)) { |
| throw e; |
| } |
| removeFailed = true; |
| } |
| } else { |
| env.rollbackTransactionAndClear(em); |
| } |
| verify(removeFailed, "remove succeeded on a detached instance"); |
| verifyExistenceInNodeTable(existing.getId()); |
| verifyExistenceInDescriptionTable(description.getId()); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| /* If X is a removed entity, it is ignored by the remove operation. */ |
| @Test |
| public void testSimpleCascadeRemoved() throws SQLException { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| try { |
| // one-to-many relationship |
| CascadingNode parent = new CascadingNode(301, null); |
| CascadingNode child = new CascadingNode(302, null); |
| env.beginTransaction(em); |
| em.persist(parent); |
| em.persist(child); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| parent = em.find(CascadingNode.class, parent.getId()); |
| em.remove(parent); |
| child = em.find(CascadingNode.class, child.getId()); |
| parent.addChild(child); |
| verify(!em.contains(parent), "Parent not removed"); |
| verify(em.contains(child), "Child not managed"); |
| em.remove(parent); |
| env.commitTransactionAndClear(em); |
| verifyAbsenceFromNodeTable(parent.getId()); |
| verifyExistenceInNodeTable(child.getId()); |
| // one-to-one relationship |
| parent = new CascadingNode(303, null); |
| CascadingNodeDescription description = new CascadingNodeDescription(304, null, "description"); |
| env.beginTransaction(em); |
| em.persist(parent); |
| em.persist(description); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| parent = em.find(CascadingNode.class, parent.getId()); |
| em.remove(parent); |
| description = em.find(CascadingNodeDescription.class, description.getId()); |
| parent.setDescription(description); |
| verify(!em.contains(parent), "Parent not removed"); |
| verify(em.contains(description), "Description not managed"); |
| em.remove(parent); |
| env.commitTransactionAndClear(em); |
| verifyAbsenceFromNodeTable(parent.getId()); |
| verifyExistenceInDescriptionTable(description.getId()); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| @ToBeInvestigated |
| public void testCircularCascade() throws SQLException { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| try { |
| CascadingNode node1 = new CascadingNode(401, null); |
| CascadingNode node2 = new CascadingNode(402, node1); |
| node2.addChild(node1); |
| node1.setParent(node2); |
| env.beginTransaction(em); |
| em.persist(node1); |
| env.commitTransactionAndClear(em); |
| verifyExistenceInNodeTable(node1.getId()); |
| verifyExistenceInNodeTable(node2.getId()); |
| env.beginTransaction(em); |
| node1 = em.find(CascadingNode.class, node1.getId()); |
| node2 = em.find(CascadingNode.class, node2.getId()); |
| em.remove(node1); |
| env.commitTransactionAndClear(em); |
| verifyAbsenceFromNodeTable(node1.getId()); |
| verifyAbsenceFromNodeTable(node2.getId()); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| private void verifyExistenceInNodeTable(int nodeId) throws SQLException { |
| verify(checkForExistenceInNodeTable(nodeId), "no node with id " + nodeId + " found using JDBC."); |
| } |
| |
| private void verifyAbsenceFromNodeTable(int nodeId) throws SQLException { |
| verify(!checkForExistenceInNodeTable(nodeId), "node with id " + nodeId + " still found using JDBC."); |
| } |
| |
| private boolean checkForExistenceInNodeTable(int nodeId) throws SQLException { |
| Connection conn = getEnvironment().getDataSource().getConnection(); |
| try { |
| PreparedStatement stmt = conn.prepareStatement("select ID, PARENT from TMP_CASC_NODE where ID = ?"); |
| try { |
| stmt.setInt(1, nodeId); |
| ResultSet rs = stmt.executeQuery(); |
| try { |
| return rs.next(); |
| } finally { |
| rs.close(); |
| } |
| } finally { |
| stmt.close(); |
| } |
| } finally { |
| conn.close(); |
| } |
| } |
| |
| private void verifyExistenceInDescriptionTable(int descId) throws SQLException { |
| verify(checkForExistenceInDescriptionTable(descId), "no description with id " + descId + " found using JDBC."); |
| } |
| |
| private void verifyAbsenceFromDescriptionTable(int descId) throws SQLException { |
| verify(!checkForExistenceInDescriptionTable(descId), "description with id " + descId + " still found using JDBC."); |
| } |
| |
| private boolean checkForExistenceInDescriptionTable(int descId) throws SQLException { |
| Connection conn = getEnvironment().getDataSource().getConnection(); |
| try { |
| PreparedStatement stmt = conn.prepareStatement("select ID, DESC_TEXT from TMP_CASC_NODE_DESC where ID = ?"); |
| try { |
| stmt.setInt(1, descId); |
| ResultSet rs = stmt.executeQuery(); |
| try { |
| return rs.next(); |
| } finally { |
| rs.close(); |
| } |
| } finally { |
| stmt.close(); |
| } |
| } finally { |
| conn.close(); |
| } |
| } |
| } |