scala jdbc/scalikejdbc
Scalikejdbc 를 이용해 DB 데이터 읽기
wefree
2021. 11. 14. 21:35
문제
MySQL test.person 테이블 스키마와 들어 있는 데이터는 아래와 같다.
CREATE table person
(
name varchar(128),
age INT
)
name | age |
a | 10 |
b | 20 |
test.person 에 저장된 데이터를 scalikejdbc 를 이용해 읽어보자
코드
build.sbt 에 아래 라이브러리를 추가한다.
libraryDependencies ++= Seq(
"org.scalikejdbc" %% "scalikejdbc" % "4.0.0",
"org.scalikejdbc" %% "scalikejdbc-config" % "4.0.0",
"org.postgresql" % "postgresql" % "42.5.0",
"mysql" % "mysql-connector-java" % "8.0.27"
)
코드 작성
connectionTimeoutMillis 는 pool.borrow() timeout 값으로 필요에 따라 충분히 크게 설정한다.
poolConnectionTimeoutMillis defines the amount of time a query will wait to acquire a connection before throwing an exception. This used to be called connectionTimeoutMillis
import scalikejdbc._
case class Person(name: String, age: Int)
object Person extends SQLSyntaxSupport[Person] {
override def tableName: String = "person"
def apply(rs: WrappedResultSet): Person = Person(
rs.string("name"),
rs.int("age")
)
}
object ScalikeTest {
def main(args: Array[String]): Unit = {
Class.forName("org.postgresql.Driver")
// Class.forName("com.mysql.jdbc.Driver")
val settings = ConnectionPoolSettings(
initialSize = 4,
maxSize = 20,
connectionTimeoutMillis = 120000L,
validationQuery = "select 1"
)
ConnectionPool.singleton("jdbc:postgresql://localhost:5432/postgres", "user_id", "user_password", settings)
// ConnectionPool.singleton("jdbc:mysql://db.host:3306/test", "user_id", "user_password", settings)
implicit val session = AutoSession
// DB.autoCommit(), DB.localTx()
val entities: List[Person] = DB.readOnly(implicit session => sql"select * from person".map(rs => Person(rs)).list().apply())
// val entities: List[Person] = sql"select * from person".map(rs => Person(rs)).list().apply()
for {
e <- entities
} println(s"${e.name} ${e.age}")
}
}
출력 결과
a 10
b 20
설명
val name = "Alice"
val personAge: Option[Int] = DB.readOnly { implicit session =>
sql"select age from person where name = ${name}" // don't worry, prevents SQL injection
.map(rs => rs.int("age")) // extracts values from rich java.sql.ResultSet
.single // single, list, traversable
.apply() // Side effect!!! runs the SQL using Connection
}
Internals
기본적으로 아래와 같은 구조이다.
Global object 인 ConnectionPool 에 각각의 connection pool 을 이름(예: MyPool) 과 함께 등록하고, NamedDB 로 사용한다.
import scalikejdbc._
Class.forName("...")
val settings: ConnectionPoolSettings = ConnectionPoolSettings(...)
ConnectionPool.add("MyPool", ..., settings)
implicit val session: DBSession = NamedAutoSession("MyPool")
NamedDB("MyPool").???
보통 connection pool 을 하나만 등록하기 때문에 connection pool 이름을 default 로 지정할 수 있다.
라이브러리 차원에서 connection pool 이 하나일 때 아래처럼 편히 쓰도록 지원한다. (내부적으로 pool 이름을 default 로 처리)
import scalikejdbc._
Class.forName("...")
val settings: ConnectionPoolSettings = ConnectionPoolSettings(...)
ConnectionPool.singleton(..., settings)
// ConnectionPool.add("default", ..., settings)
implicit val session: DBSession = AutoSession
// implicit val session: DBSession = NamedAutoSession("default")
DB.readOnly.???
// NamedDB("MyPool").readOnly.???