Patterns and Practices in Hibernate Patrycja Wegrzynowicz Yon - - PowerPoint PPT Presentation

patterns and practices in hibernate
SMART_READER_LITE
LIVE PREVIEW

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 >


slide-1
SLIDE 1

Patterns and Practices in Hibernate

Patrycja Wegrzynowicz Yon Labs, Yon Consulting 100

slide-2
SLIDE 2

2

Agenda

> Introduction – Usage Aspect of Hibernate – CaveatEmptor Example > Object-Oriented Principles > Transactional Issues > Inheritance Strategies > Summary – Key Points to Remember

slide-3
SLIDE 3

3

Usage Aspects of Hibernate

Architecture Programming Object-Oriented

Design Relational Database Design

slide-4
SLIDE 4

4

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?

slide-5
SLIDE 5

5

Object-Oriented Principles

slide-6
SLIDE 6

6

> SOLID – Single Responsibility Principle – Open/closed Principle – Liskov Substitution Principle – Interface Segregation Principle – Dependency Inversion Principle

What is OO about?

> Data Abstraction – Data and Behaviour > Encapsulation > Inheritance > Polymorphism

slide-7
SLIDE 7

7

Is This a Good OO Model?

[…] […] […] […]

slide-8
SLIDE 8

8

Is This a Good OO Model?

[…] […] […] […]

NO! Anemic Domain Model

slide-9
SLIDE 9

9

Rich OO Model

CaveatEmptor Auction Model

[…] […] […] […]

slide-10
SLIDE 10

10

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

slide-11
SLIDE 11

11

We Need Encapsulation!

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)

slide-12
SLIDE 12

12

Without Encapsulation Business Methods Are Only Lipstick on a Pig

slide-13
SLIDE 13

13

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

slide-14
SLIDE 14

14

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

slide-15
SLIDE 15

15

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!

slide-16
SLIDE 16

16

Practices to Code for Encapsulation

Consistent Management of Internal State

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; }

slide-17
SLIDE 17

17

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

slide-18
SLIDE 18

18

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

slide-19
SLIDE 19

19

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()); ?

slide-20
SLIDE 20

20

Transactional Issues

slide-21
SLIDE 21

21

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);

slide-22
SLIDE 22

22

CaveatEmptor – Concurrency Scenario

Thread A – new bid 9999 Thread B – new bid 1000

Two bids in the database: 100, 124

slide-23
SLIDE 23

23

CaveatEmptor – Concurrency Scenario

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

slide-24
SLIDE 24

24

CaveatEmptor – Concurrency Scenario

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

slide-25
SLIDE 25

25

CaveatEmptor – Concurrency Scenario

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

slide-26
SLIDE 26

26

CaveatEmptor – Concurrency Scenario

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

slide-27
SLIDE 27

27

CaveatEmptor – Concurrency Scenario

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

slide-28
SLIDE 28

28

Even Worse – Depending on Isolation Level…

// 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

slide-29
SLIDE 29

29

Bid currentMinBid = itemDAO.getMinBid(itemId); Bid currentMaxBid = itemDAO.getMaxBid(itemId); // uses load with Lock.UPGRADE Item item = itemDAO.findById(itemId, true);

Where Is the Problem?

Bid newBid = item.placeBid(userDAO.findById(userId, false), newAmount, currentMaxBid, currentMinBid);

Reordering Works!

slide-30
SLIDE 30

30

Bid currentMinBid = itemDAO.getMinBid(itemId); Bid currentMaxBid = itemDAO.getMaxBid(itemId); // uses load with Lock.UPGRADE Item item = itemDAO.findById(itemId, true);

Where Is the Problem?

Bid newBid = item.placeBid(userDAO.findById(userId, false), newAmount, currentMaxBid, currentMinBid);

Reordering Works!

slide-31
SLIDE 31

31

Is Re-Ordering the Best Aproach?

> 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

slide-32
SLIDE 32

32

Alternative Aproaches

> 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

slide-33
SLIDE 33

33

Alternative Aproaches

Inverse Mappings with Embedded Queries

<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

slide-34
SLIDE 34

34

More Puzzlers  CaveatEmptor getItemMaxBid

select b from Bid b where b.amount.value = (select max(b.amount.value) from Bid b where b.item.id = :itemid)

slide-35
SLIDE 35

35

More Puzzlers  CaveatEmptor – Check Highest Bid

// 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; }

slide-36
SLIDE 36

36

Inheritance Strategies

slide-37
SLIDE 37

37

Inheritance Strategies

> Table per class hierarchy – One table > Table per subclass – Four tables > Table per concrete class – Two tables A <<abstract>> B <<abstract>> C D

slide-38
SLIDE 38

38

Inheritance Strategies in Hibernate

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

+ + + + + + ! ! ÷

slide-39
SLIDE 39

39

Summary

slide-40
SLIDE 40

40

Summary

> 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…

slide-41
SLIDE 41

Patrycja Wegrzynowicz patrycja@YonLabs.com Yon Labs, Yon Consulting http://YonLabs.com