Other than executing business methods , most of the work done by your entity bean involves keeping its state in synch with its corresponding data in the database. This work is done by your ejbLoad and ejbStore methods. Implementing ejbLoadThe container calls ejbLoad when an entity is activated and needs to guarantee that its state in memory matches what's in the database. Listing 6.7 shows the ejbLoad method for EnglishAuctionBean . Listing 6.7 ejbLoad “A BMP ejbLoad Method Retrieves Data from the Databasepackage com.que.ejb20.auction.model; ... public class EnglishAuctionBean extends AbstractEntity implements EntityBean { ... public void ejbLoad() { Connection con = null; PreparedStatement stmt = null; ResultSet rs = null; try { con = BMPHelper.getConnection("auctionSource"); // build a select statement for the auction fields stmt = con.prepareStatement("SELECT Name, Description, Status, " + "StartingBid, MinBidIncrement, ReserveAmount, StartDate, " + "ScheduledEndDate, ActualEndDate, LeadingBidId, WinningBidId, " + "ItemId, Quantity FROM auction WHERE id = ?"); Integer primaryKey = (Integer)ctx.getPrimaryKey(); stmt.setInt(1, primaryKey.intValue()); // execute the select rs = stmt.executeQuery(); boolean found = rs.next(); if (found) { // transfer the result set values into the instance fields id = primaryKey; name = rs.getString("Name"); description = rs.getString("Description"); status = rs.getString("Status"); // the SQL numeric type is returned as a BigDecimal BigDecimal bd = rs.getBigDecimal("StartingBid"); startingBid = bd != null ? new Double(bd.doubleValue()) : null; bd = rs.getBigDecimal("MinBidIncrement"); minBidIncrement = bd != null ? new Double(bd.doubleValue()) : null; bd = rs.getBigDecimal("ReserveAmount"); reserveAmount = bd != null ? new Double(bd.doubleValue()) : null; startDateTime = rs.getTimestamp("StartDate"); scheduledEndDateTime = rs.getTimestamp("ScheduledEndDate"); actualEndDateTime = rs.getTimestamp("ActualEndDate"); leadingBidId = (Integer)rs.getObject("LeadingBidId"); winningBidId = (Integer)rs.getObject("WinningBidId"); itemId = (Integer)rs.getObject("ItemId"); quantity = (Integer)rs.getObject("Quantity"); // will load the item when requested item = null; // will load the bids when requested setBids(null); leadingBid = null; winningBid = null; } else { throw new EJBException("Error loading data for auction id " + primaryKey); } } catch (SQLException e) { // throw a system exception if a database access error occurs throw new EJBException; } finally { // close the connection BMPHelper.cleanup(stmt, con); } } ... } Within ejbLoad , you can use an instance's EntityContext to get the primary key assigned to it. You can then build a select statement that retrieves the entity's attribute values from the database. An entity also needs to retrieve its associated objects or at least load the key values it can use to access them when they're needed. Chapter 17, "Addressing Performance," covers some options you can employ with BMP but for now, the ejbLoad method for the auction defers any reading of its list of bids until they're needed. The leading and winning bids and the auction's item assignment are simply held as primary key values at this point as well. This is the nice thing about BMP: You can decide what you want to load at this point quite easily. Implementing ejbStoreThe counterpart to ejbLoad is the ejbStore method. The container calls ejbStore when a transaction that includes an entity object commits or when the entity is about to be passivated. You have to provide an implementation of ejbStore that writes an entity's persistent state to the database. Listing 6.8 shows this method for EnglishAuctionBean . Listing 6.8 ejbStore “A BMP ejbStore Method Writes an Entity's State to the Databasepackage com.que.ejb20.auction.model; ... public class EnglishAuctionBean extends AbstractEntity implements EntityBean { ... private Collection bidsToStore = new ArrayList(); ... public void ejbStore() { Connection con = null; PreparedStatement stmt = null; try { con = BMPHelper.getConnection("auctionSource"); if (!bidsToStore.isEmpty()) { // store bids added during this transaction stmt = con.prepareStatement( "INSERT INTO bid (id, AuctionId, BidderId, BidDateTime, Amount, " + "TransactionId) VALUES (?,?,?,?,?,?)"); Iterator iter = bidsToStore.iterator(); while (iter.hasNext()) { Bid newBid = (Bid)iter.next(); stmt.setInt(1, newBid.getId().intValue()); stmt.setInt(2, newBid.getAuctionId().intValue()); stmt.setInt(3, newBid.getBidderId().intValue()); stmt.setTimestamp(4, newBid.getDateTimeSubmitted()); stmt.setDouble(5, newBid.getAmount().doubleValue()); stmt.setString(6, newBid.getTransactionId()); int rowsInserted = stmt.executeUpdate(); if (rowsInserted != 1) { throw new EJBException( "Could not insert bid into the database"); } } bidsToStore.clear(); stmt.close(); } // build an update statement to write the auction state to the database stmt = con.prepareStatement("UPDATE auction SET Name = ?, " + "Description = ?, Status = ?, StartingBid = ?, MinBidIncrement = ?, " + "ReserveAmount = ?, StartDate = ?, ScheduledEndDate = ?, " + "ActualEndDate = ?, LeadingBidId = ?, WinningBidId = ?, ItemId = ?, " + "Quantity = ? FROM auction WHERE id = ?"); stmt.setString(1, getName()); stmt.setString(2, getDescription()); stmt.setString(3, getStatus()); if (getStartingBid() != null) { stmt.setDouble(4, getStartingBid().doubleValue()); } else { stmt.setNull(4, java.sql.Types.DOUBLE); } if (getMinBidIncrement() != null) { stmt.setDouble(5, getMinBidIncrement().doubleValue()); } else { stmt.setNull(5, java.sql.Types.DOUBLE); } if (getReserveAmount() != null) { stmt.setDouble(6, getReserveAmount().doubleValue()); } else { stmt.setNull(6, java.sql.Types.DOUBLE); } stmt.setTimestamp(7, getStartDateTime()); stmt.setTimestamp(8, getScheduledEndDateTime()); stmt.setTimestamp(9, getActualEndDateTime()); if (getLeadingBid() != null) { stmt.setInt(10, getLeadingBid().getId().intValue()); } else { stmt.setNull(10, java.sql.Types.INTEGER); } if (getWinningBid() != null) { stmt.setInt(11, getWinningBid().getId().intValue()); } else { stmt.setNull(11, java.sql.Types.INTEGER); } if (getItemId() != null) { stmt.setInt(12, getItemId().intValue()); } else { stmt.setNull(12, java.sql.Types.INTEGER); } if (getQuantity() != null) { stmt.setInt(13, getQuantity().intValue()); } else { stmt.setNull(13, java.sql.Types.INTEGER); } // set the primary key for the WHERE clause stmt.setInt(14, getId().intValue()); // execute the update and throw an exception if it fails int rowsUpdated = stmt.executeUpdate(); if (rowsUpdated != 1) { throw new EJBException("Error storing data for auction id " + id); } } catch (SQLException e) { // throw a system exception if a database access error occurs throw new EJBException; } finally { // close the connection BMPHelper.cleanup(stmt, con); } } ... } Besides updating the state of the auction, ejbStore also inserts any new Bid objects created for the auction into the database. The complete listing for EnglishAuctionBean included on the CD shows how bidsToStore is used by the submitBid method. Listing 6.9 shows the implementation of Bid . Listing 6.9 Bid.java “Dependent Object Implementationpackage com.que.ejb20.auction.model; /** * Title: Bid<p> * Description: An auction bid<p> */ import java.sql.Timestamp; import com.que.ejb20.auction.view.BidView; public class Bid { private Integer id; private Integer auctionId; private Integer bidderId; private Timestamp dateTimeSubmitted; private Double amount; private String transactionId; public Bid() { } public Bid(Integer newId, Integer newAuctionId, Integer newBidderId, Timestamp newDateTimeSubmitted, Double newAmount, String newTransactionId) { setId(newId); setAuctionId(newAuctionId); setBidderId(newBidderId); setDateTimeSubmitted(newDateTimeSubmitted); setAmount(newAmount); setTransactionId(newTransactionId); } public Integer getId() { return id; } protected void setId(Integer newId) { if (newId != null) { id = newId; } else { throw new IllegalArgumentException("Bid id must be non-null"); } } public Integer getAuctionId() { return auctionId; } public void setAuctionId(Integer newAuctionId) { if (newAuctionId != null) { auctionId = newAuctionId; } else { throw new IllegalArgumentException("Bid auction id must be non-null"); } } public Integer getBidderId() { return bidderId; } public void setBidderId(Integer newBidderId) { if (newBidderId != null) { bidderId = newBidderId; } else { throw new IllegalArgumentException("Bid bidder id must be non-null"); } } public Timestamp getDateTimeSubmitted() { return dateTimeSubmitted; } public void setDateTimeSubmitted(Timestamp newDateTimeSubmitted) { if (newDateTimeSubmitted != null) { dateTimeSubmitted = newDateTimeSubmitted; } else { throw new IllegalArgumentException("Bid time must be non-null"); } } public Double getAmount() { return amount; } public void setAmount(Double newAmount) { if ((newAmount != null) && (newAmount.doubleValue() >= 0.0)) { amount = newAmount; } else { throw new IllegalArgumentException( "Bid amount cannot be null or negative"); } } public String getTransactionId() { return transactionId; } public void setTransactionId(String newTransactionId) { if (newTransactionId != null) { transactionId = newTransactionId; } else { throw new IllegalArgumentException("Bid transaction id must be non-null"); } } public BidView getView() { BidView view = new BidView(getAuctionId(), getBidderId(), getDateTimeSubmitted(), getAmount(), getTransactionId()); return view; } } As you can see, the most complicated part of ejbStore for the auction entity is checking for potential null values for its attributes and then calling the appropriate methods of PreparedStatement . Besides inserting new bids, an auction's associations are taken care of as long as you write all the foreign key values out to the database. The problem with this simple implementation of ejbStore is that it isn't very efficient. It always writes to the database when it's called, and it always writes every attribute even if all of them haven't changed. You'll see alternatives to this discussed in Chapter 17. |