660 likes | 903 Views
Patterns and Anti-Patterns in Hibernate. Patrycja Wegrzynowicz CTO, 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)
E N D
Patterns and Anti-Patterns in Hibernate Patrycja Wegrzynowicz CTO, 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 Aspectsof Hibernate Architecture Object-Oriented Design Relational DB Design Programming
CaveatEmptor Demo • Java Persistence with Hibernate • Christian Bauer • Gavin King • CaveatEmptor • Demo in the book • Simple auction system
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? transaction
CaveatEmptor – ConcurrentScenario Two bids in the database: 100, 124 • 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 = 124 • Item 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 = 124 • Item item = itemDAO.findById(itemId, true); • Bid newBid = item.placeBid(…, newAmount, • curMaxBid, curMinBid); • // commit transaction 1 2 3 4 successful bid: 1000 successful bid: 9999
How to Fix It? REPEATABLE_READS NO HIBERNATE VERSIONING NO SERIALIZABLE YES YES
CaveatEmptor – Hibernate Versioning Two bids in the database: 100, 124 • 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 = 124 • Item 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 = 124 • Item item = itemDAO.findById(itemId, true); • Bid newBid = item.placeBid(…, newAmount, • curMaxBid, curMinBid); • // commit transaction (version 3) 1 2 3 4 successful bid: 1000 successful bid: 9999
CaveatEmptor – Repeatable Reads Two bids in the database: 100, 124 • 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 = 124 • Item 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 = 124 • Item item = itemDAO.findById(itemId, true); • Bid newBid = item.placeBid(…, newAmount, • curMaxBid, curMinBid); • // commit transaction (version 3) 1 2 3 4 successful bid: 1000 successful bid: 9999
CaveatEmptor - Repeatable Reads • Even worse • MySQL takes a snapshot of the database at the moment of the first query
CaveatEmptor - Serializable • SERIALIZABLE works fine • Alltransactions 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 • 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
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
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? […] […] […] […]
Is This a Good OO Model? […] […] Anemic Domain Model! […] […]
Rich OO Model – CaveatEmptor […] […] […] […]
We Need Encapsulation! We can add to the mutable list whatever we like. We can add a bid to the item with a 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 // 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 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
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
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 - Hint item.setBids(item.getBids())?
Puzzle #3 Immutable List - Answer item.setBids(item.getBids())? The answer: bids = empty list
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 HibernateSlow? • Sometimes… • Depending on usage scenarios