Patterns and Practices in Hibernate Patrycja Wegrzynowicz Yon Labs, Yon Consulting 100
Agenda > Introduction – Usage Aspect of Hibernate – CaveatEmptor Example > Object-Oriented Principles > Transactional Issues > Inheritance Strategies > Summary – Key Points to Remember 2
Usage Aspects of Hibernate Architecture Object-Oriented Relational Database Design Design Programming 3
CaveatEmptor Example > Java Persistence with Hibernate – Christian Bauer, Gavin King > CaveatEmptor – A demo application with the book – A simple auction system What is good and what is bad about CaveatEmptor? 4
Object-Oriented Principles 5
What is OO about? > Data Abstraction > SOLID – Data and Behaviour – Single Responsibility Principle > Encapsulation – Open/closed Principle > Inheritance – Liskov Substitution Principle > Polymorphism – Interface Segregation Principle – Dependency Inversion Principle 6
Is This a Good OO Model? […] […] […] […] 7
Is This a Good OO Model? […] […] NO! […] Anemic Domain Model […] 8
Rich OO Model CaveatEmptor Auction Model […] […] […] […] 9
CaveatEmptor Auction Model – A Closer Look Bid-related members of Item class private List<Bid> bids = new ArrayList<Bid>(); private Bid successfulBid; private Map<Long,Bid> bidsByIdentifier = new HashMap<Long,Bid>(); public List<Bid> getBids() public void addBid(Bid bid) public Bid getSuccessfulBid() public void setSuccessfulBid(Bid successfulBid) public Map<Long, Bid> getBidsByIdentifier() public void setBidsByIdentifier(Map<Long, Bid> bidsByIdentifier) public Bid placeBid(User bidder, MonetaryAmount bidAmount, Bid currentMaxBid, Bid currentMinBid) throws BusinessException 10
We Need Encapsulation! We can add to this mutable list whatever we like public List<Bid> getBids() { return bids; } public void addBid(Bid bid) { if (bid == null ) throw new IllegalArgumentException("Can't add a null Bid."); this. getBids().add(bid); // Don't have to set the "other" side, a Bid can only be instantiated with a given item } We can add a bid with a different Item instance (Item constructor is public) 11
Without Encapsulation Business Methods Are Only Lipstick on a Pig 12
Exposed Structure Results in BAD Things > Inconsistent state of objects – The winning bid for an item which is not maximal – Buyer different that the bidder of the winning bid – The winning bid added after the auction ended – Basically, we cannot trust our data! > Various Bugs – Null pointer exceptions – Nasty bugs (unexpected nulls, consider embedded objects!) > Too much code – Duplicated code – Defensive code – Spaghetti code 13
Practices to Design for Encapsulation > Business Logic First – Business methods first – No matter whether a legacy or a new database, don’t do hibernate mappings in the early stage > Lazy Programming of Getters and Setters – Lean approach – No waste – No getters and setters by default, add them when there is a real need 14
Practices to Code for Encapsulation Restrive Access Modifiers > private, package, protected > fields, getters, setters – used by hibernate or to manage referential integrity > hibernate is able to deal with any access modifiers (even private!) of the members of a class Thank god hibernate is flexible! 15
Practices to Code for Encapsulation Consistent Management of Internal State public Item(MonetaryAmount initialPrice) { // beware of polymorphic calls in constructors // it’s better to use a private _setInitialPrice from both the setter and ctr setInitialPrice(initialPrice); } public void setInitialPrice(MonetaryAmount initialPrice) { if (initialPrice == null ) throw new IllegalArgumentException(„initial price cant be null”); this .initialPrice = initialPrice; } All roads lead to Rome 16
Practices to Code for Encapsulation Do Not Expose Mutable Internal State > Problems mainly in the case of Date and Collection hierarchies – return date; // Date – return bids; // List<Bid> – Uncontrolled changes to the internal state of an object > Defensive copying – return new Date(created.getTime()); – return new ArrayList(bids); – Inefficient for collections – we force the retrieval of the bids from the database > Immutable – return Collections.unmodifiableList(bids); – Inefficient for persistent collections mapped by property as dirty checking compares collections by identity 17
Practices to Code for Encapsulation Immutable Collection Returned > Dirty checking compares identity of collections – Additional statements issued – Embedded objects – recreation of the collection – List of entities – all entities updated > Practices – Internally, do not create new collections, reuse the one retrieved – Use field mapping, or – Use different accessors for public access and hibernate access 18
Practices to Code for Encapsulation Immutable Collection Puzzler public class Item { // … public getBids() { return Collections.unmodifiableList(bids); } public setBids(List<Bid> bids) { this. bids.clear(); if (bids != null ) this. bids.addAll(bids); } // … } item.setBids(item.getBids()); ? 19
Transactional Issues 20
CaveatEmptor – PlaceBidAction Bid currentMinBid = itemDAO.getMinBid(itemId); Bid currentMaxBid = itemDAO.getMaxBid(itemId); // uses load with Lock.UPGRADE Item item = itemDAO.findById(itemId, true ); Bid newBid = item.placeBid(userDAO.findById(userId, false ), newAmount, currentMaxBid, currentMinBid); 21
CaveatEmptor – Concurrency Scenario Two bids in the database: 100, 124 Thread A – new bid 9999 Thread B – new bid 1000 22
CaveatEmptor – Concurrency Scenario Two bids in the database: 100, 124 Thread A – new bid 9999 Thread B – new bid 1000 1 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 23
CaveatEmptor – Concurrency Scenario Two bids in the database: 100, 124 Thread A – new bid 9999 Thread B – new bid 1000 1 2 // two bids in db: 100, 124 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 // curMax = 124 24
CaveatEmptor – Concurrency Scenario Two bids in the database: 100, 124 Thread A – new bid 9999 Thread B – new bid 1000 1 2 // two bids in db: 100, 124 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 // curMax = 124 3 // item with two bids: 100, 124 // curMax = 124 Item item = itemDAO.findById(itemId, true ); Bid newBid = item.placeBid(…, newAmount, curMaxBid, curMinBid); successful bid: 9999 25
CaveatEmptor – Concurrency Scenario Two bids in the database: 100, 124 Thread A – new bid 9999 Thread B – new bid 1000 1 2 // two bids in db: 100, 124 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 // curMax = 124 3 4 // item with two bids: 100, 124 // item with three bids: 100, 124, 9999 // curMax = 124 // but curMax = 124 Item item = itemDAO.findById(itemId, true ); Item item = itemDAO.findById(itemId, true ); Bid newBid = item.placeBid(…, newAmount, Bid newBid = item.placeBid(…, newAmount, curMaxBid, curMinBid); curMaxBid, curMinBid); successful bid: 9999 successful bid: 1000 26
CaveatEmptor – Concurrency Scenario Two bids in the database: 100, 124 Thread A – new bid 9999 Thread B – new bid 1000 1 2 // two bids in db: 100, 124 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 // curMax = 124 3 4 // item with two bids: 100, 124 // item with three bids: 100, 124, 9999 // curMax = 124 // but curMax = 124 Item item = itemDAO.findById(itemId, true ); Item item = itemDAO.findById(itemId, true ); Bid newBid = item.placeBid(…, newAmount, Bid newBid = item.placeBid(…, newAmount, currentMaxBid, currentMinBid); currentMaxBid, currentMinBid); successful bid: 9999 successful bid: 1000 27
Even Worse – Depending on Isolation Level… 4 // item with three bids: 100, 124, 9999 // but curMax = 124 Item item = itemDAO.findById(itemId, true ); Bid newBid = item.placeBid(…, newAmount, currentMaxBid, currentMinBid); successful bid: 1000 READ_COMMITED REPEATABLE_READS BID1 (amount 100, bid_position 0) BID1 (amount 100, bid_position 0) BID2 (amount 124, bid_position 1) BID2 (amount 124, bid_position 1) BID3 (amount 9999, bid_position 2) BID3 (amount 9999, bid_position 2 ) BID4 (amount 1000, bid_position 3) BID4 (amount 1000, bid_position 2 ) next time hibernate will load only 3 bids for item 28
Recommend
More recommend