Siêu thị PDFTải ngay đi em, trời tối mất

Thư viện tri thức trực tuyến

Kho tài liệu với 50,000+ tài liệu học thuật

© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

manning Hibernate in Action phần 9 doc
MIỄN PHÍ
Số trang
47
Kích thước
186.8 KB
Định dạng
PDF
Lượt xem
713

manning Hibernate in Action phần 9 doc

Nội dung xem thử

Mô tả chi tiết

Licensed to Jose Carlos Romero Figueroa <jose.romero@galicia.seresco.es>

302 CHAPTER 8

Writing Hibernate applications

try {

if (s == null) {

s = sessionFactory.openSession();

threadSession.set(s);

}

} catch (HibernateException ex) {

throw new InfrastructureException(ex);

}

return s;

}

public static void closeSession() { E

try {

Session s = (Session) threadSession.get();

threadSession.set(null);

if (s != null && s.isOpen())

s.close();

} catch (HibernateException ex) {

throw new InfrastructureException(ex);

}

}

public static void beginTransaction() { F

Transaction tx = (Transaction) threadTransaction.get();

try {

if (tx == null) {

tx = getSession().beginTransaction();

threadTransaction.set(tx);

}

} catch (HibernateException ex) {

throw new InfrastructureException(ex);

}

}

public static void commitTransaction() { G

Transaction tx = (Transaction) threadTransaction.get();

try {

if ( tx != null && !tx.wasCommitted()

&& !tx.wasRolledBack() )

tx.commit();

threadTransaction.set(null);

} catch (HibernateException ex) {

rollbackTransaction();

throw new InfrastructureException(ex);

}

}

public static void rollbackTransaction() { H

Transaction tx = (Transaction) threadTransaction.get();

try {

threadTransaction.set(null);

if ( tx != null && !tx.wasCommitted()

&& !tx.wasRolledBack() ) {

Licensed to Jose Carlos Romero Figueroa <jose.romero@galicia.seresco.es>

303

B

C

D

E

F

G

H

Designing layered applications

tx.rollback();

}

} catch (HibernateException ex) {

throw new InfrastructureException(ex);

} finally {

closeSession();

}

}

}

The Session of the current thread is stored in this ThreadLocal variable.

We use one database transaction for all operations, so we use another ThreadLocal

for the Transaction. Both Session and Transaction are now associated with the

thread, and many action executions in a thread can participate in the same data￾base transaction.

The getSession() method has been extended to use the thread-local variable; we

also wrap the checked HibernateException in an unchecked InfrastructureEx￾ception (part of CaveatEmptor).

We also wrap the exceptions thrown by Session.close() in this static helper

method.

The code used to start a new database transaction is similar to the getSession()

method.

If committing the database transaction fails, we immediately roll back the transac￾tion. We don’t do anything if the transaction was already committed or rolled back.

After rolling back the database transaction, the Session is closed.

This utility class is much more powerful than our first version: It provides thread￾local sessions and database transactions, and it wraps all exceptions in a runtime

exception defined by our application (or framework). This simplifies exception

handling in application code significantly, and the thread-local pattern gives us the

flexibility to share a single session among all actions and JSPs in a particular thread.

The same is true for database transactions: You can either have a single database

transactions for the whole thread or call beginTransaction() and commitTransac￾tion() whenever you need to.

You can also see that calling getSession() for the first time in a particular thread

opens a new Session. Let’s now discuss the second part of the thread-local session

Licensed to Jose Carlos Romero Figueroa <jose.romero@galicia.seresco.es>

304 CHAPTER 8

Writing Hibernate applications

design pattern: closing the Session after the view is rendered, instead of at the end

of each execute() method.

We implement this second part using a servlet filter. Other implementations are

possible, however; for example, the WebWork2 framework offers pluggable inter￾ceptors we could use. The job of the servlet filter is to close the Session before the

response is sent to the client (and after all views are rendered and actions are exe￾cuted). It’s also responsible for committing any pending database transactions. See

the doFilter() method of this servlet filter in listing 8.4.

Listing 8.4 The doFilter() method closes the Hibernate Session

public void doFilter(ServletRequest request,

ServletResponse response,

FilterChain chain)

throws IOException, ServletException {

try {

chain.doFilter(request, response);

HibernateUtil.commitTransaction();

} finally {

HibernateUtil.closeSession();

}

}

We don’t start a database transaction or open a session until an action requests

one. Any subsequent actions, and finally the view, reuse the same session and trans￾action. After all actions (servlets) and the view are executed, we commit any pend￾ing database transaction. Finally, no matter what happens, we close the Session to

free resources.

Now, we can simplify our action’s execute() method to the following:

public void execute() {

// Get values from request

try {

HibernateUtil.beginTransaction();

Session session = HibernateUtil.getSession();

// Load requested Item

// Check auction still valid

// Check amount of Bid

// Add new Bid to Item

// Place new Bid in scope for next page

// Forward to showSuccess.jsp page

Licensed to Jose Carlos Romero Figueroa <jose.romero@galicia.seresco.es>

Designing layered applications 305

} catch (HibernateException ex) {

throw new InfrastructureException(ex);

} catch (Exception ex) {

// Throw application specific exception

}

}

We’ve reduced the exception-handling code to a single try/catch block. We can

safely rethrow checked exceptions such as HibernateException as runtime excep￾tions; we can use our application’s (or framework’s) exception hierarchy.

The thread-local session pattern isn’t perfect, unfortunately. Changes made to

objects in the Session are flushed to the database at unpredictable points, and we

can only be certain that they have been executed successfully after the Transaction

is committed. But our transaction commit occurs after the view has been rendered.

The problem is the buffer size of the servlet engine: If the contents of the view

exceed the buffer size, the buffer might get flushed and the contents sent to the

client. The buffer may be flushed many times when the content is rendered, but

the first flush also sends the HTTP status code. If the SQL statements executed at

transaction commit time were to trigger a constraint violation in the database, the

user might already have seen a successful output! We can’t change the status code

(for example, use a 500 Internal Server Error), because it’s already been sent to

the client (as 200 OK).

There are several ways to prevent this rare exception: You could adjust the buffer

size of your servlet engine, or flush the Hibernate session before forwarding/redi￾recting to the view (add a flushSession() helper method to HibernateUtil). Some

web frameworks don’t immediately fill the response buffer with rendered content;

they use their own OutputStream and flush it with the response only after the view

has been completely rendered. So, we consider this a problem only with plain Java

servlet programming.

Our action is already much more readable. Unfortunately, it still mixes

together three distinctly different responsibilities: pageflow, access to the persis￾tent store, and business logic. There is also a catch clause for the HibernateExcep￾tion that looks misplaced. Let’s address the last responsibility first, since it’s the

most important.

Creating "smart" domain models

The idea behind the MVC pattern is that control logic (in our application, this is

pageflow logic), view definitions, and business logic should be cleanly separated.

Currently, our action contains some business logic—code that we might even be

Tải ngay đi em, còn do dự, trời tối mất!