patterns and anti-patterns in hibernate patrycja wegrzynowicz cto, yonita, inc. cto, yon labs and...
TRANSCRIPT
Patterns and Anti-Patterns in Hibernate
Patrycja WegrzynowiczCTO, Yonita, Inc.CTO, Yon Labs and Yon Consulting, LLC
About me
Past• 10+ years of practical experience as software developer, architect, and
head of software R&D
• PhD in Computer Science (automated code analysis)
• Speaker at JavaOne, JavaZone, Devoxx, Jazoon, OOPSLA, and others
Present• Founder and CTO of Yonita Inc. and Yon Consulting
• Bridge the gap between the industry and the academia
Future• Who cares? Seize the day!
Agenda
Transactional issues
Break
Object-oriented issues
Break
Performance issues
Break
Automated detection
Usage Aspects of Hibernate
Architecture
Object-OrientedDesign
Relational DB
Design
Programming
CaveatEmptor Demo
Java Persistence with
Hibernate• Christian Bauer
• Gavin King
CaveatEmptor• Demo in the book
• Simple auction system
TRANSACTIONAL ISSUES
First and Foremost, Correctness
Warranty of correctness• A piece of code always produces an expected result
Scenarios• Various inputs
• Concurrency
• Time line
Is CaveatEmptor Correct?
tran
sact
ion
CaveatEmptor – Concurrent Scenario
Thread A – new bid 9999
// begin transaction
// two bids in the db: 100, 124
Bid curMinBid = itemDAO.getMinBid(itemId);
Bid curMaxBid = itemDAO.getMaxBid(itemId);
// curMaxBid = 124
// get locked item// item with two bids: 100, 124
// curMaxBid = 124Item item = itemDAO.findById(itemId, true);
Bid newBid = item.placeBid(…, newAmount,
curMaxBid, curMinBid);
// commit transaction
Thread B – new bid 1000
// begin transaction
// two bids in db: 100, 124
Bid curMinBid = itemDAO.getMinBid(itemId);
Bid curMaxBid = itemDAO.getMaxBid(itemId);
// curMaxBid = 124
// get locked item// item with three bids: 100, 124, 9999
// but curMaxBid = 124Item item = itemDAO.findById(itemId, true);Bid newBid = item.placeBid(…, newAmount,
curMaxBid, curMinBid);
// commit transaction
Two bids in the database: 100, 124
1 2
3 4
successful bid: 9999 successful bid: 1000
How to Fix It?
REPEATABLE_READS
SERIALIZABLE
HIBERNATE VERSIONING
YES
NO
NO
YES
CaveatEmptor – Hibernate Versioning
Thread A – new bid 9999
// begin transaction
// two bids in the db: 100, 124
Bid curMinBid = itemDAO.getMinBid(itemId);
Bid curMaxBid = itemDAO.getMaxBid(itemId);
// curMaxBid = 124
// get item (version 1)// item with two bids: 100, 124
// curMaxBid = 124Item item = itemDAO.findById(itemId, true);
Bid newBid = item.placeBid(…, newAmount,
curMaxBid, curMinBid);
// commit transaction (version 2)
Thread B – new bid 1000
// begin transaction
// two bids in db: 100, 124
Bid curMinBid = itemDAO.getMinBid(itemId);
Bid curMaxBid = itemDAO.getMaxBid(itemId);
// curMaxBid = 124
// get item (version 2)// item with three bids: 100, 124, 9999
// but curMaxBid = 124Item item = itemDAO.findById(itemId, true);Bid newBid = item.placeBid(…, newAmount,
curMaxBid, curMinBid);
// commit transaction (version 3)
Two bids in the database: 100, 124
1 2
3 4
successful bid: 9999 successful bid: 1000
CaveatEmptor – Repeatable Reads
Thread A – new bid 9999
// begin transaction
// two bids in the db: 100, 124
Bid curMinBid = itemDAO.getMinBid(itemId);
Bid curMaxBid = itemDAO.getMaxBid(itemId);
// curMaxBid = 124
// get item (version 1)// item with two bids: 100, 124
// curMaxBid = 124Item item = itemDAO.findById(itemId, true);
Bid newBid = item.placeBid(…, newAmount,
curMaxBid, curMinBid);
// commit transaction (version 2)
Thread B – new bid 1000
// begin transaction
// two bids in db: 100, 124
Bid curMinBid = itemDAO.getMinBid(itemId);
Bid curMaxBid = itemDAO.getMaxBid(itemId);
// curMaxBid = 124
// get item (version 2)// item with two bids loaded: 100, 124
// but curMaxBid = 124Item item = itemDAO.findById(itemId, true);Bid newBid = item.placeBid(…, newAmount,
curMaxBid, curMinBid);
// commit transaction (version 3)
Two bids in the database: 100, 124
1 2
3 4
successful bid: 9999 successful bid: 1000
CaveatEmptor - Repeatable Reads
Even worse• MySQL takes a snapshot of the database at the moment of the first
query
CaveatEmptor - Serializable
SERIALIZABLE works fine• All transactions occur in a completely isolated fashion; i.e., as if all
transactions in the system had executed serially, one after the other
• Snapshot isolation or lock-based concurrency control (read/write
locks released at the end of a transaction, range-locks)
What about performance?• Can you imagine eBay working this way?
CaveatEmptor - Re-Ordering
CaveatEmptor - Re-Ordering
Item class
• Represents item and
auction; has the list of bids
Why to inject min and max
bids (internal state) to an
item?
Back to OO Principles
• Objects should contain
their internal state
Alternative Approaches
Calculate maximum in Java
• Safe, but requires loading of all the bids
Keep track of the winning bid
• Manual tracking, but with encapsulation
• Db denormalized, but fast
Utilize custom mappings with custom queries,
laziness, order or where clauses
• Db normalized, as fast as the present code
18
Alternative AproachesInverse 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
Puzzle #1 getItemMaxBid
select b
from Bid b
where b.amount.value =
(select max(b.amount.value)
from Bid b
where b.item.id = :itemid)
Puzzle #1 getItemMaxBid – Hint
Item #1• Bid $100
• Bid $125
• Bid $150
Item #2• Bid $20
• Bid $125
select b
from Bid b
where b.amount.value =
(select max(b.amount.value)
from Bid b
where b.item.id = :itemid)
Puzzle #1 getItemMaxBid – Answer
Item #1• Bid $100
• Bid $125
• Bid $150
Item #2• Bid $20
• Bid $125
getItemMaxBid(Item#2)• Item #1 Bid $125
• Item #2 Bid $125
select b
from Bid b
where b.amount.value =
(select max(b.amount.value)
from Bid b
where b.item.id = :itemid)
Puzzle #1 getItemMaxBid – Fixed
Item #1• Bid $100
• Bid $125
• Bid $150
Item #2• Bid $20
• Bid $125
getItemMaxBid(Item#2)• Item #2 Bid $125
select b
from Bid b
where b.amount.value =
(select max(b.amount.value)
from Bid b
where b.item.id = :itemid)
–and b.item.id = :itemid
Puzzle #2 placeBid
Puzzle #2 placeBid – Hint
Puzzle #2 placeBid – Fixed
>=
OBJECT-ORIENTED ISSUES
What is OO about?
Basic Principles
• Data abstraction = data + behavior
• Encapsulation, inheritance, polimorphism
Advanced Principles (SOLID)
• Single Responsibility Principle
• Open/Closed Principle
• Liskov Substitution Principle
• Interface Segregation Principle
• Dependency Inversion Principle
[…]
[…]
[…]
[…]
Is This a Good OO Model?
[…]
[…]
[…]
[…]
Anemic Domain Model!
Is This a Good OO Model?
Rich OO Model – CaveatEmptor
[…][…]
[…]
[…]
CaveatEmptor – A Closer Look
We Need Encapsulation!
We can add to the mutable listwhatever we like.
We can add a bid to the item witha different item reference.
Without Encapsulation, Business Methods Are Only Lipstick on a Pig
Exposed Structure Results in Bad Things
Inconsistent state of objects• The winning bid for an item which is not maximal
• 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
Practices to Code for EncapsulationRestrive 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
Practices to Code for EncapsulationConsistent Management of Internal State
All roads lead to Rome
// custom example!public Item(MonetaryAmount initialPrice) {
// beware of polymorphic calls in constructors// it’s better to use a private _setInitialPrice from both the setter and ctrsetInitialPrice(initialPrice);
}public void setInitialPrice(MonetaryAmount initialPrice) {
if (initialPrice == null) throw new IllegalArgumentException(„initial price cant be null”);this.initialPrice = initialPrice;
}
Practices to Code for EncapsulationDo 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
38
Practices to Code for EncapsulationImmutable 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
Practices to Code for Encapsulation
Business first
• First business methods, than accessors
Restrictive access modifiers
• Hibernate can deal with private/package/protected
Don't expose mutable internal state
• Problems mainly in the case of Date and Collections
• return date; // Date
• return bids; // List<Bid>
• Uncontrolled changes to the internal state of an object
Puzzle #3 Immutable List
Puzzle #3 Immutable List - Hint
item.setBids(item.getBids())?
Puzzle #3 Immutable List - Answer
item.setBids(item.getBids())?The answer: bids = empty list
PERFORMANCE ISSUES
This is Java.
Is Java Slow?
On most Intels Java is as fast or faster than C• The Gaia Satellie and Data Processing
• William O’Mullane from ESAC
• Jazoon 2010
Is Hibernate Slow?
Sometimes…• Depending on usage scenarios
Puzzle #4 Standard Collection Mapping
Puzzle #4 Standard Collection Mapping
Puzzle #4 Standard Collection Mapping
Puzzle #5 Immutable Collections
Puzzle #5 Immutable Collections – Hint
Puzzle #5 Immutable Collection
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
Inheritance Strategies in Hibernate
Constraints Direct Access Polymorphism
Table per Class Hierarchy
Sometimes impossible to add db
constrains(e.g. not null)
FastPolymorphic searchPolymorphic relations
Table per Subclass Constraints possible Slower (joins)
Polymorphic searchPolymorphic relations
Table per Concrete Class Constraints possible Fast
Polymorphic search with unions or N
queries
+
+ +
+ +
+
!
!
÷
AUTOMATED DETECTION OF HIBERNATE ISSUES
Code Level
Code Level Analysis
Code Level Analysis
Automated Detection of Code Issues
+
Automated code analysis
Definions of transactional,security, performance anti-
patterns
Semantic Code Query System
Code query systems• Explore relationships among program elements
• Structural and call-related predicates
• CodeQuest, JQuery
Semantic code query systems• Focus on behavior (in-depth data flow analysis)
• Behavioral predicates in addition to structural ones
• D-CUBED – my PhD thesis (focus on design patterns)
• Yonita – rewrite + queries
Yonita
Automated code analysis
• Semantic code query system
• Focus on behavior, not only structure
• Metamodel of a program: instances, structure, behaviour
(calls, input/output values, execution paths)
Definitions of various issues/anti-patterns
• Catalog of issues
• Transactions, object-oriented design, security, performance,
multithreading
How does Yonita work?
Analyses
Parser
Store
Query
BytecodeSources
asm, Recoder
analyses: structural, call flow, data flowtransitive closures
relational (MySQL) or deductive (XSB) database
SQL or Prolog(anti-)patternsbugsvulnerabilities...
Metamodel
Structural elements• Very similar to CodeQuest and Jquery
• Statements
Instances• Symbolic instances
Behavioral predicates supporting call and data flows• Output and input values
• Assignments
Instances
Null
New
This
Parameter
Exception
Multiple
ReturnInstance
ParamInstance
ThisInstance
NewInstance
NullInstance
Except.Instance
Instance Instance …
Instance … Instance …
Summary
Proper usage of hibernate is tricky• Transactional issues and correctness of code under various scenarios
• Maintanability and basic object-oriented principles
• Efficiency of code and mappings
Automatization can help
Contact
Patrycja• [email protected]
• twitter.com/YonLabs
Yonita• http://www.Yonita.com
• http://www.YonLabs.com
• http://www.YonConsulting.com