|
Spring. A developer's Notebook Authors: Take B., Gehtland J Published year: 2005 Pages: 47-49/90 |
|
|
6.6. Testing a Service with Side EffectsOf course, you didn't learn anything about the way our code used the services. To test the services, you'll need to attach them to real code and exercise the code. In your test, you can merely look for a side effect. When you use AOP, the business objects don't always get exercised with basic out-of-container tests. You've got to run some tests in the container to make sure that transactions get committed or rolled back as they should, and that the security is behaving appropriately. Remember, the context is part of the program! 6.6.1. How do I do that?You'll simply exercise some code that's using an AOP service and you'll look for a known side-effect. Example 6-19 is a test case that causes an exception in the faade. We assert that the changes did not get committed. Example 6-19. ControllerTest.java
public void testAddBadBike() throws Exception {
int origCount = store.getBikes().size();
// collision on uniquely indexed serial number 11111
Bike bike = new Bike(-1, "MyMan", "MyBike",
12, "11111", 12.00, "New");
try {
store.saveBike(bike);
} catch (Exception ex) {
assertEquals(origCount, store.getBikes().size());
return;
}
fail("Should have errored out on bad insert.");
}
6.6.2. What just happened ?You once again fired a test case from within the container. The test case called our faade directly. The faade did a database insert, but the bad data forced the transaction to roll back. Your test case made sure that the data did not get committed by doing a second read. In the next chapter, you'll release the full power of AOP using some of Spring's pre-built interceptors . If you've never seen AOP or interceptors before, this chapter might have been challenging for you. If you want to learn more about aspect-oriented programming or design patterns for proxying interfaces, turn to these resources:
|
|
|
|
|
Chapter 7. Transactions and SecurityIn mountain biking and kayaking, the most thrilling moves are running ledges. It doesn't start that way in either sport. With kayaks, you tend to do a whole lot of work to set yourself up for a few seconds of freefall. You line up against a landmark, stroke hard to build speed, push your body forward, and then simultaneously thrust with your hips and paddle off of the edge, which launches you. With bikes, you tend to pick a line through that won't smash your chain ring, line your bike up, balance yourself behind the saddle so you don't fly over the handlebars, roll forward slowly, compress your shocks, jump up to lighten the bike, and hope for the best while you're falling. |
|
|
|
|
7.1. Programmatic TransactionsLike most other services in Spring, you can use transactions programmatically or declaratively . When you're using a new service, it often makes sense to understand that service programmatically within a method before you break it out as a declarative service. Sometimes, you want to hide services, and Spring certainly makes it easy to do so. Other times, you want to be explicit, such as when transactional logic is the central job of your method. Spring makes it easy to do transaction logic programmatically. 7.1.1. How do I do that?You'll use the same mechanism for transactions as you do for JDBC: templates. For programmatic transactions, you'll use a TRansactionTemplate . You'll put all of the code in the transaction body that you want to execute together. In this case, imagine that a customer wants to cancel her current reservation and make a new one for later in the week. If the new reservation fails for any reason, she wants to keep her old one. You'll add a method to RentABike called transferReservation . It will delete an existing reservation and create a new one (Example 7-1). Example 7-1. HibRentABike.java
public void transferReservation(final Reservation oldRes,
final Reservation newRes) throws ReservationTransferException {
TransactionTemplate template =
new TransactionTemplate(this.transactionManager);
template.setPropagationBehavior(
TransactionDefinition.PROPAGATION_REQUIRED);
try {
template.execute(new TransactionCallbackWithoutResult( ) {
protected void doInTransactionWithoutResult(
TransactionStatus transactionStatus) {
getHibernateTemplate( ).save(newRes);
getHibernateTemplate( ).delete(oldRes);
}
});
} catch (Exception ex) {
throw new ReservationTransferException( );
}
}
You'll need to set up the transaction strategy for the template in the context, and create the accounts and the RentABike (Example 7-2). Example 7-2. RentABike-servlet.xml
<beans>
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="mappingResources">
<list>
<value>com/springbook/Bike.hbm.xml</value>
<value>com/springbook/Customer.hbm.xml</value>
<value>com/springbook/Reservation.hbm.xml</value>
<value>com/springbook/LogEvent.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
net.sf.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.
hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
<bean id="rentaBike" class="com.springbook.HibRentABike">
<property name="storeName">
<value>Bruce's Bikes</value>
</property>
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/rentabike</value>
</property>
<property name="username"><value>bikestore</value></property>
</bean>
</beans>
Take special care. Because you are using MySQL as the database for this example, you'll need one final step to get it to work. In order to support transactions, you have to mark your tables in MySQL as InnoDB type, which lets MySQL be transactional, with full ACID semantics. Note: ACID stands for atomic, consistent, isolated and durable. All transactions need these properties . 7.1.2. What just happened ?A template is a simple, default transaction-handling method. All of the boilerplate code is in the template. You just have to implement the method that does all of the work. It will either all succeed or roll back. You're looking for two things: a pluggable architecture, and leverage. Of course, Hibernate transactions need only a command or two. Think about a native implementation. If you wanted to replace the Hibernate transactions with JTA transactions (to coordinate with changes in another database), you'd have to replace your implementation throughout your code, and you'd have to do much more work. With your Spring implementation, you need only change configuration. The template gives you exactly what you want: a place to specify what needs to happen together successfully for the transaction to succeed. |
|
|
|
Spring. A developer's Notebook Authors: Take B., Gehtland J Published year: 2005 Pages: 47-49/90 |
![]() Hibernate: A Developer's Notebook | ![]() Maven: A Developer's Notebook (Developer's Notebooks) | ![]() JBoss: A Developer's Notebook | ![]() Spring Recipes: A Problem-Solution Approach | ![]() Spring in Action |