Upload
others
View
62
Download
1
Embed Size (px)
Citation preview
Java & SQL Stronger Together
@MarkusWinand @ModernSQL
Picture: Africa Studio@Shutterstock
“We can solve any problem by introducing an extra
level of abstraction” (Based on the “fundamental theorem of software engineering”)
Database
DatabaseJDBC
Provides unique API for different SQL-
Databases
DatabaseJDBCJPA
Provides unique API for different SQL-
Databases
Maps objects from/to tables
DatabaseJDBCJPA
Spring Data JPA
Provides unique API for different SQL-
Databases
Maps objects from/to tables
Makes common tasks very simple
DatabaseJDBCJPA
Spring Data JPAYour Code
Provides unique API for different SQL-
Databases
Maps objects from/to tables
Makes common tasks very simple
DatabaseJDBCJPA
Spring Data JPAYour Code
Provides unique API for different SQL-
Databases
Maps Objects from/to tables
Makes common tasks very simple
90%?
Doesn’t provide 100% of underlying
functionality
DatabaseJDBCJPA
Spring Data JPAYour Code
Provides unique API for different SQL-
Databases
Maps Objects from/to tables
Makes common tasks very simple
90%?Proprietary data types? —> (VendorCls)jdbcCls
DatabaseJDBCJPA
Spring Data JPAYour Code
Provides unique API for different SQL-
Databases
Maps Objects from/to tables
Makes common tasks very simple
90%?Proprietary data types? —> (VendorCls)jdbcCls
The processing is not about objects? —> QueryBuilder, JPQL
or native queries
DatabaseJDBCJPA
Spring Data JPAYour Code
Provides unique API for different SQL-
Databases
Maps Objects from/to tables
Makes common tasks very simple
90%?Proprietary data types? —> (VendorCls)jdbcCls
The processing is not about objects? —> QueryBuilder, JPQL
or native queries
“Hibernate’s design goal is to relieve the developer
from 95% of common data persistence-related
programming tasks[…] Hibernate does not hide
the power of SQLfrom you[…]”
— Hibernate ORM User Guide (second paragraph)
DatabaseJDBCJPA
Spring Data JPAYour Code
Provides unique API for different SQL-
Databases
Maps Objects from/to tables
Makes common tasks very simple
90%?Proprietary data types? —> (VendorCls)jdbcCls
The processing is not about objects? —> QueryBuilder, JPQL
or native queries
DatabaseJDBCJPA
Spring Data JPAYour Code
Provides unique API for different SQL-
Databases
Maps Objects from/to tables
Makes common tasks very simple
90%?Proprietary data types? —> (VendorCls)jdbcCls
The processing is not about objects? —> QueryBuilder, JPQL
or native queries
findBy… not powerful enough? —> QueryBuilder, JPQL
or native queries
What is theright level of abstraction
for any given problem?
Pattern #1: Lists
1.2.3.
Go for a walk in the woods
Picnic
Improve SQL-foo
BUCKET LISTSpring
DatabaseJDBCJPA
Spring Data JPAYour Code
List: All Attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECT*FROMtbl
List: All Attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
ResultSetrs=c.prepareStatement("SELECT*"+"FROMtbl").executeQuery();
SELECT*FROMtbl
List: All Attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<Entity>c=em.createQuery("FROMEntity").getResultList();
ResultSetrs=c.prepareStatement("SELECT*"+"FROMtbl").executeQuery();
SELECT*FROMtbl
List: All Attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<Entity>c=em.createQuery("FROMEntity").getResultList();
Collection<Entity>c=r.findAll();
ResultSetrs=c.prepareStatement("SELECT*"+"FROMtbl").executeQuery();
SELECT*FROMtbl
List: All Attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
List: Some Attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa1,…FROMtbl
List: Some Attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa1,…FROMtbl
List: Some Attributes
Runtime improvement: ~0% - 10 000%
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa1,…FROMtbl
List: Some Attributes
Runtime improvement: ~0% - 10 000%
Affects:
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa1,…FROMtbl
List: Some Attributes
Runtime improvement: ~0% - 10 000%
Affects:- Java: GC
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa1,…FROMtbl
List: Some Attributes
Runtime improvement: ~0% - 10 000%
Affects:- Java: GC- Network
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa1,…FROMtbl
List: Some Attributes
Runtime improvement: ~0% - 10 000%
Affects:- Java: GC- Network- Database:
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa1,…FROMtbl
List: Some Attributes
Runtime improvement: ~0% - 10 000%
Affects:- Java: GC- Network- Database:
- Index Only Scan
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa1,…FROMtbl
List: Some Attributes
Runtime improvement: ~0% - 10 000%
Affects: - Java: GC - Network - Database:
- Index Only Scan - Mem + CPU (e.g. sorting)
DatabaseJDBCJPA
Spring Data JPAYour Code
ResultSetrs=c.prepareStatement("SELECTa1,…"+"FROMtbl").executeQuery();
SELECTa1,…FROMtbl
List: Some Attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECTnewDTO(a1,…)"+"FROMEnt").getResultList();
ResultSetrs=c.prepareStatement("SELECTa1,…"+"FROMtbl").executeQuery();
SELECTa1,…FROMtbl
List: Some Attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECTnewDTO(a1,…)"+"FROMEnt").getResultList();
ResultSetrs=c.prepareStatement("SELECTa1,…"+"FROMtbl").executeQuery();
SELECTa1,…FROMtbl
List: Some Attributes
Or: LAZY attributes
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECTnewDTO(a1,…)"+"FROMEnt").getResultList();
ResultSetrs=c.prepareStatement("SELECTa1,…"+"FROMtbl").executeQuery();
SELECTa1,…FROMtbl
List: Some Attributes
Or: LAZY attributes
<P>Collection<P>findAllProjectedBy(Class<P>p);
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECTnewDTO(a1,…)"+"FROMEnt").getResultList();
ResultSetrs=c.prepareStatement("SELECTa1,…"+"FROMtbl").executeQuery();
SELECTa1,…FROMtbl
List: Some Attributes
Or: LAZY attributes
<P>Collection<P>findAllProjectedBy(Class<P>p);
publicinterfaceProj{…getA1();…}
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECTnewDTO(a1,…)"+"FROMEnt").getResultList();
ResultSetrs=c.prepareStatement("SELECTa1,…"+"FROMtbl").executeQuery();
SELECTa1,…FROMtbl
List: Some Attributes
Or: LAZY attributes
<P>Collection<P>findAllProjectedBy(Class<P>p);Collection<Proj>c=r.findAllProjectedBy(Proj.class);
publicinterfaceProj{…getA1();…}
DatabaseJDBCJPA
Spring Data JPAYour Code
List: Attributes of Multiple Objects
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,b.a1,…FROMtblaJOINtbl2b
List: Attributes of Multiple Objects
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECT"+"newDTO(a.a1,b.a1,…)"+"FROMEntityaJOINa.childb").getResultList();
SELECTa.a1,b.a1,…FROMtblaJOINtbl2b
List: Attributes of Multiple Objects
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECT"+"newDTO(a.a1,b.a1,…)"+"FROMEntityaJOINa.childb").getResultList();
SELECTa.a1,b.a1,…FROMtblaJOINtbl2b
List: Attributes of Multiple Objects
<P>Collection<P>findAllProjectedBy(Class<P>p);Collection<Proj>c=r.findAllProjectedBy(Proj.class);
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECT"+"newDTO(a.a1,b.a1,…)"+"FROMEntityaJOINa.childb").getResultList();
SELECTa.a1,b.a1,…FROMtblaJOINtbl2b
List: Attributes of Multiple Objects
<P>Collection<P>findAllProjectedBy(Class<P>p);Collection<Proj>c=r.findAllProjectedBy(Proj.class);
publicinterfaceProj{…getA1();Proj2getB();}
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECT"+"newDTO(a.a1,b.a1,…)"+"FROMEntityaJOINa.childb").getResultList();
SELECTa.a1,b.a1,…FROMtblaJOINtbl2b
List: Attributes of Multiple Objects
<P>Collection<P>findAllProjectedBy(Class<P>p);Collection<Proj>c=r.findAllProjectedBy(Proj.class);
publicinterfaceProj{…getA1();Proj2getB();}
Still selects all columns
DATAJPA-1218
DatabaseJDBCJPA
Spring Data JPAYour Code
Collection<DTO>c=em.createQuery("SELECT"+"newDTO(a.a1,b.a1,…)"+"FROMEntityaJOINa.childb").getResultList();
SELECTa.a1,b.a1,…FROMtblaJOINtbl2b
List: Attributes of Multiple Objects
@Query("SELECT"+"newDTO(a.a1,b.a1,…)"+"FROMEntityaJOINa.childb")Collection<DTO>findAllDtoProj();Collection<DTO>c=r.findAllDtoProj();
publicinterfaceProj{…getA1();Proj2getB();}
Still selects all columns
DATAJPA-1218
DatabaseJDBCJPA
Spring Data JPAYour Code
List: Derived Data
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,SUM(b.a1)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data
DatabaseJDBCJPA
Spring Data JPAYour Code Collection<DTO>c=
em.createQuery("SELECT"+"newDTO(a.a1,SUM(b.a1))"+"FROMEntityaJOINa.childb"+"GROUPBYa").getResultList();
SELECTa.a1,SUM(b.a1)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data
DatabaseJDBCJPA
Spring Data JPAYour Code Collection<DTO>c=
em.createQuery("SELECT"+"newDTO(a.a1,SUM(b.a1))"+"FROMEntityaJOINa.childb"+"GROUPBYa").getResultList();
SELECTa.a1,SUM(b.a1)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data@Query("SELECT"+"newDTO(a.a1,SUM(b.a1))"+"FROMEntityaJOINa.childb"+"GROUPBYa")Collection<DTO>findAllDtoProj();Collection<DTO>c=r.findAllDtoProj();
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,SUM(b.a1),SUM(CASEWHENb.a2=1THENb.a1ELSE0END)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,SUM(b.a1),SUM(CASEWHENb.a2=1THENb.a1ELSE0END)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data
More beautifully, but rarely supported: SELECTa.a1,SUM(b.a1),SUM(b.a1)FILTER(WHEREb.a2=1)FROMtblaJOINtbl2bGROUPBYa.a1
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,SUM(b.a1),SUM(CASEWHENb.a2=1THENb.a1ELSE0END)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data
More beautifully, but rarely supported: SELECTa.a1,SUM(b.a1),SUM(b.a1)FILTER(WHEREb.a2=1)FROMtblaJOINtbl2bGROUPBYa.a1
1999
2001
2003
2005
2007
2009
2011
2013
2015
2017
2019
5.1 MariaDBMySQL
9.4 PostgreSQL1.0 3.30 SQLite
DB2 LUWOracleSQL Server
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,SUM(b.a1),SUM(CASEWHENb.a2=1THENb.a1ELSE0END)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data
JPQL doesn't allow CASE in aggregates
More beautifully, but rarely supported: SELECTa.a1,SUM(b.a1),SUM(b.a1)FILTER(WHEREb.a2=1)FROMtblaJOINtbl2bGROUPBYa.a1
1999
2001
2003
2005
2007
2009
2011
2013
2015
2017
2019
5.1 MariaDBMySQL
9.4 PostgreSQL1.0 3.30 SQLite
DB2 LUWOracleSQL Server
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,SUM(b.a1),SUM(CASEWHENb.a2=1THENb.a1ELSE0END)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data
JPQL doesn't allow CASE in aggregates
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,SUM(b.a1),SUM(CASEWHENb.a2=1THENb.a1ELSE0END)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data@NamedNativeQuery(name="Entity.findAllXy",query="<<SQL>>",resultSetMapping="…")@SqlResultSetMapping(…)publicclassEntity{…}
JPQL doesn't allow CASE in aggregates
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,SUM(b.a1),SUM(CASEWHENb.a2=1THENb.a1ELSE0END)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data@NamedNativeQuery(name="Entity.findAllXy",query="<<SQL>>",resultSetMapping="…")@SqlResultSetMapping(…)publicclassEntity{…}
Collection<DTO>c=em.createNamedQuery("Entity.findAllXy").getResultList();
JPQL doesn't allow CASE in aggregates
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECTa.a1,SUM(b.a1),SUM(CASEWHENb.a2=1THENb.a1ELSE0END)FROMtblaJOINtbl2bGROUPBYa.a1
List: Derived Data@NamedNativeQuery(name="Entity.findAllXy",query="<<SQL>>",resultSetMapping="…")@SqlResultSetMapping(…)publicclassEntity{…}
Collection<DTO>c=em.createNamedQuery("Entity.findAllXy").getResultList();
@Query(native=true)Collection<DTO>findAllXy();
JPQL doesn't allow CASE in aggregates
Application
Application
Application
Application ORMs are super useful for the Load-Change-Store
cycle on complexentity graphs.
Application ORMs are super useful for the Load-Change-Store
cycle on complexentity graphs.
If the cycle is broken,entities might not be the right level of abstraction.
Pattern #2: Search
Pattern #2: Search
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECT*FROMtbla<<supercomplexquery>>
Search: Load Entity from Native Query
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECT*FROMtbla<<supercomplexquery>>
Search: Load Entity from Native Query
@NamedNativeQuery(name="Entity.findSpecialOnes",query="<<SQL>>",resultSetMapping="…")@SqlResultSetMapping(@entities=@EntityResult(entityClass=Entity.class))publicclassEntity{…}
DatabaseJDBCJPA
Spring Data JPAYour Code
SELECT*FROMtbla<<supercomplexquery>>
Search: Load Entity from Native Query
@NamedNativeQuery(name="Entity.findSpecialOnes",query="<<SQL>>",resultSetMapping="…")@SqlResultSetMapping(@entities=@EntityResult(entityClass=Entity.class))publicclassEntity{…}
@Query(native=true)Collection<Entity>findSpecialOnes();
CREATETABLEt(idINTEGER,parentINTEGER,)
CREATETABLEt(idINTEGER,parentINTEGER,)
CREATETABLEt(idINTEGER,parentINTEGER,)
publicvoiddescendTree(Entitystart){for(Entitye:start.getChildren()){descendTree(e);}}
publicvoiddescendTree(Entitystart){for(Entitye:start.getChildren()){descendTree(e);}}
publicvoiddescendTree(Entitystart){for(Entitye:start.getChildren()){descendTree(e);}}
SELECT...FROMEntityWHEREparent=?
publicvoiddescendTree(Entitystart){for(Entitye:start.getChildren()){descendTree(e);}}
SELECT...FROMEntityWHEREparent=?
publicvoiddescendTree(Entitystart){for(Entitye:start.getChildren()){descendTree(e);}}
SELECT...FROMEntityWHEREparent=?
publicvoiddescendTree(Entitystart){for(Entitye:start.getChildren()){descendTree(e);}}
SELECT...FROMEntityWHEREparent=?
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
publicvoiddescendTree(Entitystart){for(Entitye:start.getChildren()){descendTree(e);}}
SELECT...FROMEntityWHEREparent=?
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
SELECT...FROMEntityWHEREid=?
SELECTt.id,t.parentFROMtWHEREt.id=?
SELECTt.id,t.parentFROMtWHEREt.id=?UNIONALLSELECTt.id,t.parentFROMtWHEREt.parent=?
SELECTt.id,t.parentFROMtWHEREt.id=?UNIONALLSELECTt.id,t.parentFROMtWHEREt.parent=?
SELECTt.id,t.parentFROMtWHEREt.id=?UNIONALLSELECTt.id,t.parentFROMtWHEREt.parent=?
WITHRECURSIVEprev(id,parent)AS(
)
SELECTt.id,t.parentFROMtWHEREt.id=?UNIONALLSELECTt.id,t.parentFROMtJOINprevONt.parent=prev.id
SELECT*FROMprev
WITHRECURSIVEprev(id,parent)AS(
)
SELECTt.id,t.parentFROMtWHEREt.id=?UNIONALLSELECTt.id,t.parentFROMtJOINprevONt.parent=prev.id
SELECT*FROMprev
WITHRECURSIVEprev(id,parent)AS(
)
SELECTt.id,t.parentFROMtWHEREt.id=?UNIONALLSELECTt.id,t.parentFROMtJOINprevONprev.parent=t.id
SELECT*FROMprev
Vendor Dialect
Vendor DialectStandard SQL (ISO 9075)
Vendor DialectStandard SQL (ISO 9075)
HQL
Vendor DialectStandard SQL (ISO 9075)
HQLJPQL
Vendor DialectStandard SQL (ISO 9075)
HQLJPQL
Spring .findBy…
Vendor DialectStandard SQL (ISO 9075)
HQLJPQL
Spring .findBy…
1999
2001
2003
2005
2007
2009
2011
2013
2015
2017
2019
5.1 10.2 MariaDB8.0 MySQL
8.4 PostgreSQL1.0 3.8.3 SQLite
7.0 DB2 LUW11gR2 Oracle
2005 SQL Server
* without keyword recursive
withrecursive
**
*
JVM
JVM
JVM
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
JVM
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
JVM
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
JVM
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
PersistenceContext
JVM
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
PersistenceContext
JVM
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
PersistenceContext
JVM
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
PersistenceContext
What if I run the recursive query
What if I run the recursive query
What if I run the recursive query
before my JPA logic?
JVM
PersistenceContext
JVM
PersistenceContext
@NamedNativeQuery(name="Entity.findSpecialOnes",query="<<SQL>>",resultSetMapping="…")@SqlResultSetMapping(@entities=@EntityResult(entityClass=Entity.class))publicclassEntity{…}
JVM
PersistenceContext
findSpecialOnes();//WarmUp
JVM
PersistenceContext
findSpecialOnes();//WarmUp
JVM
PersistenceContext
findSpecialOnes();//WarmUp
JVM
publicvoidascendTree(Entitye){while(e!=null){e=e.getParent();}}
PersistenceContext
findSpecialOnes();//WarmUp
A lot has happened
since SQL-92
SQL has evolved
beyond the relational idea
A lot has happened
since SQL-92
SQL has evolved
beyond the relational idea
If you use SQL for CRUD operations only, you are doing it wrong
A lot has happened
since SQL-92
SQL has evolved
beyond the relational idea
If you use SQL for CRUD operations only, you are doing it wrong
A lot has happened
since SQL-92
https://modern-sql.com @ModernSQL by @MarkusWinand