Spring.NET Data Access
Download
Report
Transcript Spring.NET Data Access
Spring.NET Data Access
Spring in a nutshell
Applies OO best practices and integrates best-ofbreed technologies to provide a “one-stop-shop”
for building high-quality enterprise applications
quickly.
Best practices are not platform specific
Spring architecture and design is powerful yet
simple to use
Proven benefits in the real world
Developer productivity
Spring Data Access Design used by VOCA (British
Clearing House) and performs 100,000,000 transactions
in a 4 hour window every weekday – no failures.
Spring Core Architecture
Manages component lifecycle and
dependencies
Centralizes and externalizes application
configuration information
Decouples business logic from infrastructure
Decorates components with cross-cutting
behavior
Spring Solutions
Solutions address major application areas and their
common problems
Client Application Development
Database Access
MS-DTC, ADO.NET, NHibrenate . . .
Remote Access
ADO.NET, iBATIS.NET, NHibernate (ORM)
Transaction Management
Web/WinForm
Web Services, Remoting
Each solution builds on the core architecture
Solutions foster integration and does no reinvent the
wheel.
Before/After comparison
Before Spring
Proprietary, buggy
configuration code
Proliferation of
singletons, service
locators (glue code)
Copy-and-paste code,
ugly exception handling
Testing? What’s that?
After Spring
One elegant way to
configure everything
Dependency Injection –
components are only
handed what they need
Cohesive reusable
business logic
Easy comprehensive
testing
IoC/Dependency Injection:
What’s the point again?
Testing becomes easy
Maintenance becomes easy
Loose coupling facilitates local changes
Clear separation of concerns improves modularity
Configuration becomes easy
You can test the component in isolation
Decoupled from application logic
Easy to vary based on context
Reuse becomes easy
Loosely coupled components can be reused
Spring Core/AOP
Core – IoC/Dependency Injection
AOP – Aspect oriented programming
Compliments OO
Empowers modularization of cross-cutting concerns
IoC+AOP = a match made in heaven
AOP Framework build on Spring IoC configuration
Out-of-the-box aspects address common concerns
Transaction Management
Spring AOP/Transactions
Example - TransacationProxyFactoryObject
Wraps a plain .NET object in a transactional proxy
Methods to advise are fully configurable
How does it work?
Interceptor intercepts each method invocation
If the method is marked transactional:
Create or reuse a transaction appropriately
Commit or rollback based on configuration policy
Will look at usage examples shortly
Spring Data Access: ADO.NET
Simpler programming model than raw ADO.NET for
common scenarios
Resource management
‘Template’/callback APIs
Transaction management
Does not limit your ability to do ‘cast down’ to vendor specific
functionality for ‘uncommon’ scenarios.
Programmatic and declarative
Exception Translation
IoC integration for configuration
Designed to create a robust Data Access Layer
Spring Data Access Features Resource Management
Spring removes the need for you to worry about
managing resources explicitly in code.
Need to manage Connection, Command, DataReader …
Traditional API usage has limitations
Using ‘using’
No ‘catch’ to do exception translation/wrapping within called
code.
Resource management is not centralized
You still better code it right everywhere you use it.
Spring Data Access Features Transaction Management
Spring removes the need for you to decide up
front what transaction manager you are using, i.e.
local or distributed for ADO.NET or ORM
transaction managers
Spring removes the need to code against a
transaction API
Can use declarative transactions to control transaction
manager (both local and distributed)
Spring’s declarative transaction management can be
driven either by annotations or externalized XML
configuration.
MS Transaction Management
Options
Local
ADO.NET (1.1)
EnterpiseServices
(1.1)
System.Transactions
(2.0)
.NET 3.0 *
The sweet spot is Declarative transaction management +
local transactions
Distributed Declarative Programmatic
Spring sits in this spot.
* only applies if provider supports “Promotable Single
Phase Enlistment” (PSPE)
Spring Transaction Managers
AdoPlatformTransactionManager
ServiceDomainPlatformTransactionManager
Distributed transactions (COM+ Services)
TxScopePlatformTransactionManager
Local transactions
Local or Distributed as needed (if PSPE supported)
Others for ORM solutions
Switching is just a small configuration change.
Callback interfaces
ITransactionCallback
Spring Data Access Features Exception Translation
Spring provides a meaningful Data Access
Exception hierarchy
MS DataSet actually provides one but not for
more general ADO.NET API usage
Not singly rooted (.NET 1.1)
Base exception + error code
(.NET 2.0)
Spring knows what ORA-917 is and translates it
to a BadSqlGrammerException
Portable across database vendors
ADO.NET Framework:
AdoTemplate
AdoTemplate is central abstraction to execute ADO.NET
operations
Contains generic ‘Execute’ methods that handle resource
and transaction management
Callback interfaces and/or delegates provide access to
underlying ‘managed’ objects such as DbCommand,
DataAdapter
Provides many convenient ‘one-liner’ methods for common
scenarios
All delegate to generic Execute methods.
AdoTemplate is stateless and threadsafe – can use a single
instance in each DAO.
AdoTemplate Execute Methods
Would only use in application code if ‘one-liners’
don’t cover your needs
Can downcast to access vendor specific
functionality
public Object Execute(ICommandCallback action);
public Object Execute(CommandDelegate del);
public Object Execute(CommandType commandType,
string sql,
IDataAdapterCallback dataAdapterCallback);
Simple usage generic callback
public int GetTestObjectCount() {
AdoTemplate adoTemplate = new AdoTemplate(dbProvider);
return adoTemplate.Execute(new TestCommandCallback());
}
public class TestCommandCallback : ICommandCallback {
public Object DoInCommand(IDbCommand cmd) {
cmd.CommandText = "SELECT COUNT(*) FROM TestObjects";
int count = (int)cmd.ExecuteScalar();
// do something with count...
return count;
}
}
Implicit transaction used in this example.
Passed in IDbCommand connection/transaction properties
are managed by Spring.
DbProvider
DbProvider is like the .NET 2.0 provider
abstraction but with additional metadata to
more easily support portability across
providers and for use in .NET 1.1
environments.
Parameter Convenience
Classes
Help build up common parameter
collections
IDbParameters
Reduce verbosity
AddOut, AddInOut, DataSet related methods
Also an alternative Parameter Builder Class
Params.Add().Type(DbType.Int32).Name(“Age”) . . .
AdoTemplate ‘one-liners’
Various overloaded methods for
ExecuteScalar
ExecuteNonQuery
Provide lightweight object mapping
Query methods that use callbacks
IRowCallback, IRowMapper, IResultSetExtractor
DataSet Support – overloaded methods for
DataSetFill
DataSetUpdate
Partial Method listing
ExecuteNonQuery
int ExecuteNonQuery(CommandType commandType,
string sql);
int ExecuteNonQuery(CommandType commandType,
string sql,
string name, Enum dbType, int size,
object parameterValue);
// output parameters can be retrieved...
int ExecuteNonQuery(CommandType commandType,
string sql,
IDbParameters parameters);
Partial method listing
DataSet
int DataSetFill(DataSet dataSet, CommandType commandType, string sql);
int DataSetFill(DataSet dataSet, CommandType commandType, string sql,
IDbParameters parameters);
int DataSetFill(DataSet dataSet, CommandType commandType, string sql,
string[] tableNames);
int DataSetFill(DataSet dataSet, CommandType commandType, string sql,
DataTableMappingCollection tableMapping);
int DataSetUpdate(DataSet dataSet,
string tableName,
IDbCommand insertCommand,
IDbCommand updateCommand,
IDbCommand deleteCommand);
Simple Example
public class TestObjectDao : AdoDaoSupport,
ITestObjectDao {
public void Create(string name, int age) {
AdoTemplate.ExecuteNonQuery(
String.Format(CommandType.Text,
"insert into TestObjects(Age, Name) "
+ "VALUES ({0}, '{1}')", age, name));
}
}
<object id="testObjectDao"
type=“MyApp.DAL.TestObjectDao,MyApp.DAL">
<property name="ConnectionString"
value="Data Source=(local);Database=Spring;
UserID=${UserId};Password=springqa;
Trusted_Connection=False"/>
</object>
DAO Support/Configuration
AdoDaoSupport class in previous example
provides basic boiler-plate properties such
as connection string/DbProvider and
AdoTemplate properties + some simple
convenience mehods
Note use of ${UserID} in connection string
Values are replaced with those from a namevalue section in App.config
standard Spring.Net configuration functionality
More Complex Example
public interface IAccountManager {
void DoTransfer(float creditAmount, float debitAmount);
}
public interface IAccountDebitDao {
void DebitAccount(float debitAmount);
}
public interface IAccountCreditDao {
void CreateCredit(float creditAmount);
}
public interface IAccountManager {
void DoTransfer(float creditAmount, float debitAmount);
}
More Complex Example II
public class AccountCreditDao : AdoDaoSupport,IAccountCreditDao {
public void CreateCredit(float creditAmount) {
AdoTemplate.ExecuteNonQuery(CommandType.Text,
String.Format("insert into Credits(creditAmount) VALUES ({0})",
creditAmount));
}
}
public class AccountDebitDao : AdoDaoSupport,IAccountDebitDao {
public void DebitAccount(float debitAmount) {
AdoTemplate.ExecuteNonQuery(CommandType.Text,
String.Format("insert into Debits (DebitAmount) VALUES ({0})",
debitAmount));
}
}
More Complex Example III
public class AccountManager : IAccountManager{
private IAccountCreditDao creditDao;
private IAccountDebitDao debitDao;
// Properties for field members...
[Transaction()]
public void DoTransfer(float creditAmount, float debitAmount)
{
creditDao.CreateCredit(creditAmount);
debitDao.DebitAccount(debitAmount);
}
}
Transaction Management
In configuration file
Each DAO can point to a different database
Switch from local to distributed transaction manager
No code changes!
If use XML based demarcation can more easily
change transaction boundaries
Maybe at first demarcate on DAO objects since there is
no business logic – simple ‘CRUD’ app.
Over time need service layer – move demarcation to
service layer that groups multiple DAOs.
AdoOperations
OO model for DB operations
AdoQuery - Result set mapping to objects
AdoNonQuery- Insert/Update/Delete
AdoScalar – Return single value
StoredProcedure
Preferred approach
Also thread safe and reusable
out parameters and multiple result sets
Can derive parameters from metadata.
DataSetOperations – CRUD for DataSets (ala VS.NET
generated adapters)
Use of ICommandCreater implementation for efficient
parameter re-creation.
AdoNonQuery
public class CreateTestObjectNonQuery : AdoNonQuery {
private static string sql =
"insert into TestObjects(Age,Name) values (@Age,@Name)";
public CreateTestObjectNonQuery(IDbProvider dbProvider)
: base(dbProvider, sql) {
DeclaredParameters.Add("Age", DbType.Int32);
DeclaredParameters.Add("Name", SqlDbType.NVarChar, 16);
Compile();
}
public void Create(string name, int age) {
ExecuteNonQuery(name, age);
}
Variable length arguments
}
Map result set to objects
public class TestObjectQuery : MappingAdoQuery {
private static string sql =
"select TestObjectNo, Age, Name from TestObjects";
public TestObjectQuery(IDbProvider dbProvider)
: base(dbProvider, sql) {
CommandType = CommandType.Text;
}
NullMappingDataReader
protected override object MapRow(IDataReader reader,
int num) {
TestObject to = new TestObject();
to.ObjectNumber = reader.GetInt32(0);
to.Age = reader.GetInt32(1);
to.Name = reader.GetString(2);
return to;
}
}
NullMappingDataReader
TestObjectQuery objectQuery = new TestObjectQuery(dbProvider);
IList objectList = objectQuery.Query();
//now have a list of TestObject instances
IDataReaderWrapper
NullMappingDataReader implementation
Specified in AdoTemplate
Say goodbye to code like this…
to.ObjectNumber = (!reader.IsDBNull(0)) ? reader.GetInt32(0) : -1;
to.Age = (!reader.IsDBNull(1)) ? reader.GetInt32(1) : -1;
to.Name = (!reader.IsDBNull(2)) ? reader.GetString(2) : String.Empty;
Stored Procedure
public class CallCreateTestObject : StoredProcedure {
public CallCreateTestObject(IDbProvider dbProvider)
: base(dbProvider, "CreateTestObject") {
DeriveParameters();
Compile();
}
public void Create(string name, int age) {
ExecuteNonQuery(name, age);
}
}
Stored Procedure
Easy Access to out parameters
public class CallCreateTestObject : StoredProcedure {
public CallCreateTestObject(IDbProvider dbProvider)
: base(dbProvider, "CreateTestObject") {
DeriveParameters();
Compile();
}
public void Create(string name, int age) {
IDictionary inParams = new Hashtable();
inParams["name"] = name;
inParams["age"] = age;
IDictionary outParams = ExecuteNonQuery(inParams);
}
}
Stored Procedure Class Value
Proposition
Use of DeriveParameters and
StoredProcedure base class leads to very
terse code.
Monitoring
Can apply logging/performance timer aspects to
AdoTemplate and OO database objects,
AdoNonQuery.
Non invasive approach
Isolates the cross cutting concern of logging and
performance monitoring
Very accommodating to change
Today log, tomorrow use custom perf counter – core
code doesn’t change.
Summary
Spring’s approach to Data Access is very
applicable to .NET development
ADO.NET framework will give you real
productivity gains