Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Alternatives to JPA 2018 - JDBC Based

Alternatives to JPA 2018 - JDBC Based

Alternatives to JPA - jdbc based

Avatar for Sunghyouk Bae

Sunghyouk Bae

May 09, 2025
Tweet

More Decks by Sunghyouk Bae

Other Decks in Programming

Transcript

  1. Agenda • What is ORM • Pros / Cons of

    JPA • Current status in Coupang • Alternatives of JPA • Slick • jOOQ • Exposed • Requery
  2. ORM (Object Relational Mapping) • OOP’s object graph vs Relational

    Database • Focus OOP, not Relational Database • No matter of RDBMS vendor - Same code • Hibernate coverage is 95% over traditional SQL statements • ORM not suitable for data centric application in performance • Why should you use an ORM?
  3. Pros of JPA • Focus to Java Object graph &

    OOP • No need to know relations and constraints of entities • No need to know specific DB features by various vendor • No need to know SQL, just use Java API • Supplement by HQL or JPQL or QueryDSL • All support for Stateful, Stateless (Default is Stateful)
  4. Cons of JPA • If you knew SQL already, JPA

    is wired • Hard to learning (exponential) • Low performance by stateful and fetch by id • No suitable for Bulk or Set operations • Massive Insert, Statistical Summary (Cube …) • Non-Threadsafe Session - Low throughput • Need to learn specific JPA Vendor (Hibernate, Eclipse Link) • HQL, @DynamicInsert, @LazyCollection • 2nd Cache (recommend JCache (JSR-305))
  5. Current Status in Coupang • No Deep Dive • Missing

    override (hashCode, equals, toString) • No using @NatualId • Bad Policy / Bad Design • Every entity has Own identifier (Some case no need) • Poor performance -> Mislead “JPA is bad” • Apply not suitable case • Bulk operations, Statistical operations • No use supplement features • StatelessSession, 2nd Cache …
  6. Features in Alternatives of JPA • Design Principle • OOP

    based, Support Multi DB Vendor • No need stateful for Reference object • Support association, inheritance, converter in JPA • Performance • Speed up like Plain SQL • Stateless • Support Asynchronous or Reactive • Support Bulk or Batch operations
  7. Slick • Database access library • Not ORM -> Functional

    Relational Mapping • Bring relational model to Scala OOP • Natural fit ( no impedance mismatch) • Stateless • Matures (Slick version 3) (Non-blocking DBIO) • Essential Slick Book
  8. Slick - Query // Get Users with age > 20

    Query statement Async Operations
  9. jOOQ • Reflect Database Schema to generate Entity Class •

    Typesfe SQL (aka Typesafe MyBatis) • Database First (Not ORM) • Stateless • Need DBMS Owner Authority • jOOQ vs Hibernate : When to choose which
  10. jOOQ - typesafe SQL SELECT * FROM BOOK WHERE BOOK.PUBLISHED_IN

    = 2011 ORDER BY BOOK.TITLE create.selectFrom(BOOK) .where(BOOK.PUBLISHED_IN.eq(2011)) .orderBy(BOOK.TITLE) select().from(t).where(t.a.eq(select(t2.x).from(t2)); // Type-check here: ---------------> ^^^^ select().from(t).where(t.a.eq(any(select(t2.x).from(t2))); // Type-check here: -------------------> ^^^^ select().from(t).where(t.a.in(select(t2.x).from(t2)); // Type-check here: ---------------> ^^^^ select(t1.a).from(t1).unionAll(select(t2.a).from(t2)); // Type-check here: ----------------> ^^^^ select(t1.a, t1.b).from(t1).union(select(t2.a, t2.b).from(t2)); // Type-check here: -------------------> ^^^^^^^^^^ Predicates Set operations
  11. requery • No reflection (apt code generation) - Fast instancing

    • Fast startup & performance • Schema generation • Blocking / Non-blocking API (Reactive with RxJava) • Support partial object / refresh / upsert • Custom type converter like JPA • Compile time entity validation • Support almost JPA annotations
  12. @Entity abstract class AbstractPerson { @Key @Generated int id; @Index("name_index")

    // table specification String name; @OneToMany // relationships 1:1, 1:many, many to many Set<Phone> phoneNumbers; @Converter(EmailToStringConverter.class) Email email; @PostLoad // lifecycle callbacks void afterLoad() { updatePeopleList(); } // getter, setters, equals & hashCode automatically generated into Person.java } requery - define entity Identifier Entity class Converter Listeners
  13. Result<Person> query = data .select(Person.class) .where(Person.NAME.lower().like("b%")) .and(Person.AGE.gt(20)) .orderBy(Person.AGE.desc()) .limit(5) .get();

    Observable<Person> observable = data .select(Person.class) .orderBy(Person.AGE.desc()) .get() .observable(); requery - query Query by Fluent API Reactive Programming Cold Observable Non blocking
  14. @Entity(model = "tree")
 interface TreeNode {
 @get:Key
 @get:Generated
 val id:

    Long @get:Column
 var name: String
 
 @get:ManyToOne(cascade = [DELETE]) var parent: TreeNode?
 
 @get:OneToMany(mappedBy = "parent", cascade = [SAVE, DELETE])
 val children: MutableSet<TreeNode> } requery - self refence by Kotlin Identifier Entity class 1:N, N:1 Cascade
  15. requery - Blob/Clob usage class ByteArrayBlobConverter : Converter<ByteArray, Blob> {

    override fun getPersistedSize(): Int? = null override fun getPersistedType(): Class<Blob> = Blob::class.java override fun getMappedType(): Class<ByteArray> = ByteArray::class.java override fun convertToMapped(type: Class<out ByteArray>?, value: Blob?): ByteArray? { return value?.binaryStream?.readBytes() } override fun convertToPersisted(value: ByteArray?): Blob? { return value?.let { SerialBlob(it) } } }
  16. requery - Blob property @Entity(model = "kt") interface BigModel {

    @get:Key @get:Generated @get:Column(name = "model_id") val id: Int @get:Column(name = "model_name") var name: String? @get:Convert(value = ByteArrayBlobConverter::class) @get:Column(name = "model_picture") var picture: ByteArray? } Blob column
  17. Exposed - Kotlin SQL Framework • Lightweight SQL Library •

    Provide two layers of data access • Typesafe SQL wrapping DSL • Lightweight Data Access Object • See : First steps with Kotlin/Exposed • Cons • Not support parameterized SQL
  18. Exposed - SQL DSL object Users : Table() { val

    id = varchar("id", 10).primaryKey() // Column<String> val name = varchar("name", length = 50) // Column<String> val cityId = (integer("city_id") references Cities.id).nullable() // Column<Int?> } object Cities : Table() { val id = integer("id").autoIncrement().primaryKey() // Column<Int> val name = varchar("name", 50) // Column<String> }
  19. Exposed - SQL DSL val munichId = Cities.insert { it[name]

    = "Munich" } get Cities.id Cities.insert { it[name] = "Prague" } Users.insert { it[id] = "andrey" it[name] = "Andrey" it[cityId] = saintPetersburgId } Users.insert { it[id] = "sergey" it[name] = "Sergey" it[cityId] = munichId }
  20. Exposed - SQL DSL - Join (Users innerJoin Cities) .slice(Users.name,

    Cities.name) .select { (Users.id.eq("andrey") or Users.name.eq("Sergey")) and Users.id.eq("sergey") and Users.cityId.eq(Cities.id) } .forEach { println("${it[Users.name]} lives in ${it[Cities.name]}") }
  21. Exposed - SQL DSL - Join 2 ((Cities innerJoin Users)

    .slice(Cities.name, Users.id.count()) .selectAll() .groupBy(Cities.name)) .forEach { val cityName = it[Cities.name] val userCount = it[Users.id.count()] if (userCount > 0) { println("$userCount user(s) live(s) in $cityName") } else { println("Nobody lives in $cityName") } }
  22. Exposed - DAO object Users : IntIdTable() { val name

    = varchar("name", 50).index() val city = reference("city", Cities) val age = integer("age") } object Cities: IntIdTable() { val name = varchar("name", 50) } Schema Definition class User(id: EntityID<Int>) : IntEntity(id){ companion object : IntEntityClass<User>(Users) var name by Users.name var city by City referencedOn Users.city var age by Users.age } class City(id: EntityID<Int>) : IntEntity(id) { companion object : IntEntityClass<City>(Cities) var name by Cities.name val users by User referrersOn Users.city } Entity Definition
  23. Exposed - DAO Usage val munich = City.new { name

    = "Munich" } User.new { name = "a" city = munich age = 5 } User.new { name = "b" city = munich age = 27 } munich.users.joinToString { it.name } User.find { Users.age.between(18, 60) } One To Many All user’s name in Munich
  24. Conclusion • Already legacy database exists ? Use only Java

    • jOOQ or requery • Scala only ? -> Slick • Kotlin only ? -> requery, Exposed • No matter language? -> requery • Need Reactive programming? -> • requery with kotlinx-requery • kotlinx-rxjava2-jdbc ( we will open March )