#2576 help with database best practices

fraya Wed 9 Nov 2016

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

SlimerDude Wed 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've implemented the second and it's OK!

Thank you!!

Login or Signup to reply.