Patterns and Practices in Hibernate
Patrycja Wegrzynowicz Yon Labs, Yon Consulting 100
Patterns and Practices in Hibernate Patrycja Wegrzynowicz Yon - - PowerPoint PPT Presentation
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 >
Patrycja Wegrzynowicz Yon Labs, Yon Consulting 100
2
> Introduction – Usage Aspect of Hibernate – CaveatEmptor Example > Object-Oriented Principles > Transactional Issues > Inheritance Strategies > Summary – Key Points to Remember
3
Architecture Programming Object-Oriented
Design Relational Database Design
4
> 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?
5
6
> SOLID – Single Responsibility Principle – Open/closed Principle – Liskov Substitution Principle – Interface Segregation Principle – Dependency Inversion Principle
> Data Abstraction – Data and Behaviour > Encapsulation > Inheritance > Polymorphism
7
[…] […] […] […]
8
[…] […] […] […]
NO! Anemic Domain Model
9
[…] […] […] […]
10
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
11
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 to this mutable list whatever we like We can add a bid with a different Item instance (Item constructor is public)
12
13
> 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
14
> 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
15
> 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!
16
All roads lead to Rome
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; }
17
> 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
18
> 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
19
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()); ?
20
21
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);
22
Thread A – new bid 9999 Thread B – new bid 1000
Two bids in the database: 100, 124
23
Thread A – new bid 9999 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 Thread B – new bid 1000
Two bids in the database: 100, 124 1
24
Thread A – new bid 9999 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 Thread B – new bid 1000 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124
Two bids in the database: 100, 124 1 2
25
Thread A – new bid 9999 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 // item with two bids: 100, 124 // curMax = 124 Item item = itemDAO.findById(itemId, true); Bid newBid = item.placeBid(…, newAmount, curMaxBid, curMinBid); Thread B – new bid 1000 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124
Two bids in the database: 100, 124 1 2 3 successful bid: 9999
26
Thread A – new bid 9999 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 // item with two bids: 100, 124 // curMax = 124 Item item = itemDAO.findById(itemId, true); Bid newBid = item.placeBid(…, newAmount, curMaxBid, curMinBid); Thread B – new bid 1000 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 // item with three bids: 100, 124, 9999 // but curMax = 124 Item item = itemDAO.findById(itemId, true); Bid newBid = item.placeBid(…, newAmount, curMaxBid, curMinBid);
Two bids in the database: 100, 124 1 2 3 4 successful bid: 9999 successful bid: 1000
27
Thread A – new bid 9999 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 // item with two bids: 100, 124 // curMax = 124 Item item = itemDAO.findById(itemId, true); Bid newBid = item.placeBid(…, newAmount, currentMaxBid, currentMinBid); Thread B – new bid 1000 // two bids in db: 100, 124 Bid curMinBid = itemDAO.getMinBid(itemId); Bid curMaxBid = itemDAO.getMaxBid(itemId); // curMax = 124 // item with three bids: 100, 124, 9999 // but curMax = 124 Item item = itemDAO.findById(itemId, true); Bid newBid = item.placeBid(…, newAmount, currentMaxBid, currentMinBid);
Two bids in the database: 100, 124 1 2 3 4 successful bid: 9999 successful bid: 1000
28
// item with three bids: 100, 124, 9999 // but curMax = 124 Item item = itemDAO.findById(itemId, true); Bid newBid = item.placeBid(…, newAmount, currentMaxBid, currentMinBid);
4 successful bid: 1000
BID1 (amount 100, bid_position 0) BID2 (amount 124, bid_position 1) BID3 (amount 9999, bid_position 2) BID4 (amount 1000, bid_position 2)
next time hibernate will load only 3 bids for item
BID1 (amount 100, bid_position 0) BID2 (amount 124, bid_position 1) BID3 (amount 9999, bid_position 2) BID4 (amount 1000, bid_position 3) REPEATABLE_READS READ_COMMITED
29
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);
Reordering Works!
30
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);
Reordering Works!
31
> Well, it works, but… – All developers will need to bear the right sequence in mind – Doable, but…
code bases do nothing but grows, people change…
> Back to OO Principles – The core problem is encapsulation and self-containment! – The passed values (max/min) belong to the state of the object – Premature optimization that led to this serious bug
32
> Calculate maximum in Java – Safe – Bids are ordered – Requires to load all bids from the database > Keep track of the winning bid – Denormalization – Manual tracking, but with encapsulation is fine – Fast > Utilize inverse mappings with custom queries, laziness, ‘order’ or ‘where’ clauses – Database normalized – Automated – As fast as the present code with getMaxBid/getMinBid
33
<set name=„maxBids" inverse="true" lazy=„true"> <key not-null="true"> <column name="ITEM_ID"/> </key> <one-to-many class="Bid"/> <loader query-ref="getMaxBid"/> </set> > Automated object state – Encapsulation > Inverse – Makes use of present relations – Normalized form > Lazy – Loaded on the first access > Custom queries – SQL – Use order-by and where if they are enough
34
select b from Bid b where b.amount.value = (select max(b.amount.value) from Bid b where b.item.id = :itemid)
35
// Check highest bid (can also be a different Strategy (pattern)) if (currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0) { throw new BusinessException("Bid too low"); } public MonetaryAmount getAmount() { return amount; } public class MonetaryAmount { … public int compareTo(Object o) { if (o instanceof MonetaryAmount) { return this.getValue().compareTo(((MonetaryAmount) o).getValue()); } return 0; }
36
37
> Table per class hierarchy – One table > Table per subclass – Four tables > Table per concrete class – Two tables A <<abstract>> B <<abstract>> C D
38
Polymorphic search with unions or N queries Fast Constraints possible
Table per Concrete Class
Polymorphic search Polymorphic relations Slower (joins) Constraints possible
Table per Subclass
Polymorphic search Polymorphic relations Fast Sometimes impossible to add db constrains (e.g. not null)
Table per Class Hierarchy Polymorphism Direct Access Constraints
39
40
> Good work hibernate guys! – Hibernate is very flexible > OO Principles – Meet the basics, then think big > Transactional Issues – Full state helps – In case of critical systems, do not hesitate to use DB admin’s help > Relational Principles – There is much more about these…