Created
January 20, 2015 18:45
-
-
Save jvasileff/1f5fc3bb9b4246620142 to your computer and use it in GitHub Desktop.
Ceylon + Spring: Declarative Transactions, DI, and AOP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ceylon.collection { | |
LinkedList | |
} | |
import ceylon.dbc { | |
Sql, | |
newConnectionFromDataSource | |
} | |
import ceylon.interop.java { | |
javaClass | |
} | |
import ceylon.time { | |
Instant, | |
now | |
} | |
import javax.inject { | |
inject | |
} | |
import javax.sql { | |
DataSource | |
} | |
import org.apache.tomcat.jdbc.pool { | |
TomcatDataSource=DataSource | |
} | |
import org.aspectj.lang { | |
ProceedingJoinPoint | |
} | |
import org.aspectj.lang.annotation { | |
aspect, | |
around | |
} | |
import org.springframework.context.annotation { | |
AnnotationConfigApplicationContext, | |
configuration, | |
enableAspectJAutoProxy, | |
componentScan, | |
bean, | |
propertySource | |
} | |
import org.springframework.core.env { | |
Environment | |
} | |
import org.springframework.jdbc.datasource { | |
TransactionAwareDataSourceProxy, | |
DataSourceTransactionManager | |
} | |
import org.springframework.stereotype { | |
service, | |
component, | |
repository | |
} | |
import org.springframework.transaction { | |
PlatformTransactionManager | |
} | |
import org.springframework.transaction.annotation { | |
enableTransactionManagement, | |
transactional | |
} | |
late Instant startupTime; | |
///////////////////////////////////////////////// | |
// | |
// Run Method | |
// | |
///////////////////////////////////////////////// | |
shared void run() { | |
value ctx = AnnotationConfigApplicationContext(javaClass<AppConfig>()); | |
assert(is Application application = ctx.getBean("application")); | |
application.main(); | |
} | |
///////////////////////////////////////////////// | |
// | |
// Configuration | |
// | |
///////////////////////////////////////////////// | |
configuration | |
propertySource { | |
ignoreResourceNotFound = true; | |
\ivalue = {"classpath:/application.properties"}; | |
} | |
componentScan({"sandbox.ceylon.snap.base"}) | |
enableAspectJAutoProxy(false) | |
enableTransactionManagement | |
class AppConfig() { | |
late Environment environment; | |
inject void setEnvironment(Environment environment) | |
=> this.environment = environment; | |
shared bean default Instant startupTime() | |
=> now(); | |
shared bean default DataSource rawDataSource() { | |
value ds = TomcatDataSource(); | |
ds.driverClassName = environment.getProperty("jdbc.driver", "org.h2.Driver"); | |
ds.url = environment.getProperty("jdbc.url", "jdbc:h2:mem:db1"); | |
ds.validationQuery = environment.getProperty("jdbc.query", "select 1"); | |
if (exists username = environment.getProperty("jdbc.username")) { | |
ds.username = username; | |
} | |
if (exists password = environment.getProperty("jdbc.password")) { | |
ds.username = password; | |
} | |
return ds; | |
} | |
shared bean default TransactionAwareDataSourceProxy dataSource() | |
=> TransactionAwareDataSourceProxy(rawDataSource()); | |
shared bean default PlatformTransactionManager txManager() | |
=> DataSourceTransactionManager(rawDataSource()); | |
// TODO Review Sql's use of thread locals | |
shared bean default Sql sql() | |
=> Sql(newConnectionFromDataSource(dataSource())); | |
} | |
component aspect class AspectConfigs() { | |
around("execution(* Repository.*(..))") | |
shared Anything profile(ProceedingJoinPoint pjp) { | |
value start = system.nanoseconds; | |
try { | |
Anything result = pjp.proceed(); | |
return result; | |
} finally { | |
print(" (execution time for ``pjp.string`` " + | |
"was ``system.nanoseconds - start``ns"); | |
} | |
} | |
} | |
///////////////////////////////////////////////// | |
// | |
// Application | |
// | |
///////////////////////////////////////////////// | |
service class Application { | |
Repository repository; | |
shared inject new Application( | |
Repository repository, | |
Instant startupTime) { | |
this.repository = repository; | |
package.startupTime = startupTime; | |
} | |
shared void main() { | |
print("Application started at ``startupTime.dateTime()``"); | |
repository.initialize(); | |
repository.insertRows(); | |
repository.selectRows().each(print); | |
// transaction rollback test | |
value rowCount = repository.selectRows().size; | |
assert(size > 0); | |
try { | |
repository.deleteRows(true); | |
} | |
catch (Exception e) { /* ignore */ } | |
assert(rowCount == repository.selectRows().size); | |
print ("deleteRows() was rolled back!"); | |
// transaction commit test | |
repository.deleteRows(false); | |
assert(0 == repository.selectRows().size); | |
print ("deleteRows() was committed!"); | |
} | |
} | |
shared class Language(Integer id, String name) { | |
shared actual String string => "``id``, ``name``"; | |
} | |
interface Repository { | |
shared formal void initialize(); | |
shared formal void insertRows(); | |
shared formal void deleteRows(Boolean fail); | |
shared formal List<Language> selectRows(); | |
} | |
transactional repository class RepositorySql satisfies Repository { | |
Sql sql; | |
shared inject new RepositorySql(Sql sql) { | |
this.sql = sql; | |
} | |
shared actual void initialize() { | |
sql.Statement("create table jvm_langs( | |
id bigint primary key, | |
name varchar(100))").execute(); | |
} | |
shared actual void insertRows() { | |
value insert = "insert into jvm_langs values (?, ?)"; | |
sql.Statement(insert).execute(1, "Ceylon"); | |
sql.Statement(insert).execute(2, "Groovy"); | |
sql.Statement(insert).execute(3, "Jacl"); | |
} | |
shared actual void deleteRows(Boolean fail) { | |
sql.Statement("delete from jvm_langs").execute(); | |
if (fail) { | |
throw; // trigger rollback | |
} | |
} | |
shared actual List<Language> selectRows() { | |
value result = LinkedList<Language>(); | |
sql.Select("select id, name from jvm_langs") | |
.forEachRow()(void(row) { | |
assert (is Integer id = row["id"]); | |
assert (is String name = row["name"]); | |
result.add(Language(id, name)); | |
}); | |
return result; | |
} | |
} | |
/* | |
OUTPUT: | |
Application started at 2015-01-20T13:24:37.135 | |
(execution time for execution(Object sandbox.ceylon.snap.base.Repository.initialize()) was 25965000ns | |
(execution time for execution(Object sandbox.ceylon.snap.base.Repository.insertRows()) was 10623000ns | |
(execution time for execution(List sandbox.ceylon.snap.base.Repository.selectRows()) was 48190000ns | |
1, Ceylon | |
2, Groovy | |
3, Jacl | |
(execution time for execution(List sandbox.ceylon.snap.base.Repository.selectRows()) was 484000ns | |
(execution time for execution(Object sandbox.ceylon.snap.base.Repository.deleteRows(boolean)) was 1699000ns | |
(execution time for execution(List sandbox.ceylon.snap.base.Repository.selectRows()) was 884000ns | |
deleteRows() was rolled back! | |
(execution time for execution(Object sandbox.ceylon.snap.base.Repository.deleteRows(boolean)) was 451000ns | |
(execution time for execution(List sandbox.ceylon.snap.base.Repository.selectRows()) was 216000ns | |
deleteRows() was committed! | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment