I'm doing a small CRUD application with Sqlite. My current solution is to pass a SqlConn to a manager class (like PersonManager) and one Database class with once methods for each manager to initialize them.
Code is like this:
class PersonManager
{
SqlConn dbSession
new make(SqlConn dbSession) { this.dbSession = dbSession }
Int insert(Person p)
{
dbSession.sql ...
}
}
class Database
{
SqlConn dbSession
new make(Uri uri) // opens database
once PersonManager()
{
PersonManager(dbSession)
}
}
In the Main pass database uri as a parameter
db := Database(`test.db`)
db.person.insert(Person { ... })
it works, but I've been looking at Service and I've found this topic http://fantom.org/forum/topic/1337, about services and databases, but I can't figure out how to do it, since SqlConn is not a const type and Service is const.
The thing is that I don't know how to share the database connection between the components. What are the database best practices?
Thanks
SlimerDudeWed 9 Nov 2016
Hi Fraya!
I'm going to paraphrase your question as "How do I safely make Database a const class so I can store it in a Service?"
Well, when you introduce the concurrent pod then const really means Thread Safe... so if your app runs in the one thread, like what an fwt gui application typically would, then it is fine to wrap the SqlConn in an Unsafe:
const class Database {
private const Unsafe dbSessionRef
SqlConn dbSession {
get { dbSessionRef.val }
set { throw ReadOnlyErr() }
}
new make(Uri uri) {
this.dbSessionRef = Unsafe(...)
}
PersonManager personManager() {
// as person manager doesn't / shouldn't hold state
// it's fine & lightweight to new up on every call
PersonManager(dbSession)
}
}
However, if your app is multi-threaded, such as a web application is, then Database should only store the URL and each threaded call to personManager() should create a new SqlConn:
using concurrent::Actor
const class Database {
const Uri dbUrl
new make(Uri uri) { ... }
PersonManager personManager() {
return Actor.locals.getOrAdd("myPersonManager") |->PersonManager| {
dbSession := ...
return PersonManager(dbSession)
}
}
}
Note how in both cases, the Database class above is const.
fraya Wed 9 Nov 2016
I'm doing a small CRUD application with Sqlite. My current solution is to pass a
SqlConnto a manager class (likePersonManager) and oneDatabaseclass withoncemethods for each manager to initialize them.Code is like this:
class PersonManager { SqlConn dbSession new make(SqlConn dbSession) { this.dbSession = dbSession } Int insert(Person p) { dbSession.sql ... } } class Database { SqlConn dbSession new make(Uri uri) // opens database once PersonManager() { PersonManager(dbSession) } } In the Main pass database uri as a parameter db := Database(`test.db`) db.person.insert(Person { ... })it works, but I've been looking at
Serviceand I've found this topic http://fantom.org/forum/topic/1337, about services and databases, but I can't figure out how to do it, since SqlConn is not a const type andServiceis const.The thing is that I don't know how to share the database connection between the components. What are the database best practices?
Thanks
SlimerDude Wed 9 Nov 2016
Hi Fraya!
I'm going to paraphrase your question as "How do I safely make
Databaseaconstclass so I can store it in aService?"Well, when you introduce the
concurrentpod thenconstreally means Thread Safe... so if your app runs in the one thread, like what an fwt gui application typically would, then it is fine to wrap theSqlConnin anUnsafe:const class Database { private const Unsafe dbSessionRef SqlConn dbSession { get { dbSessionRef.val } set { throw ReadOnlyErr() } } new make(Uri uri) { this.dbSessionRef = Unsafe(...) } PersonManager personManager() { // as person manager doesn't / shouldn't hold state // it's fine & lightweight to new up on every call PersonManager(dbSession) } }However, if your app is multi-threaded, such as a web application is, then
Databaseshould only store the URL and each threaded call topersonManager()should create a newSqlConn:using concurrent::Actor const class Database { const Uri dbUrl new make(Uri uri) { ... } PersonManager personManager() { return Actor.locals.getOrAdd("myPersonManager") |->PersonManager| { dbSession := ... return PersonManager(dbSession) } } }Note how in both cases, the
Databaseclass above isconst.fraya Wed 9 Nov 2016
I've implemented the second and it's OK!
Thank you!!