Java and RDBMS Married with issues Database constraints Speaker - - PowerPoint PPT Presentation

java and rdbms
SMART_READER_LITE
LIVE PREVIEW

Java and RDBMS Married with issues Database constraints Speaker - - PowerPoint PPT Presentation

Java and RDBMS Married with issues Database constraints Speaker Jeroen van Schagen Situation store Java Relational Application Database retrieve JDBC JDBC J ava D ata b ase C onnectivity Data Access API ( java.sql ,


slide-1
SLIDE 1

Java and RDBMS

Married with issues

Database constraints

slide-2
SLIDE 2

Speaker

Jeroen van Schagen

slide-3
SLIDE 3

Situation

store retrieve

Java
 Application Relational
 Database

JDBC

slide-4
SLIDE 4
  • Java Database Connectivity
  • Data Access API ( java.sql, javax.sql )
  • JDK 1.1 (1997)
  • Relational Database
  • Many implementations

JDBC

slide-5
SLIDE 5

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (‘Jan’)”;
 statement.executeUpdate(sql);

slide-6
SLIDE 6

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (‘Jan’)”;
 statement.executeUpdate(sql);

slide-7
SLIDE 7

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

Name can only have
 up to 3 characters Name is required Name can only occur once

Database Maintain data

slide-8
SLIDE 8

Constraint types

Not null Type Length Primary key Foreign key Unique key Check

slide-9
SLIDE 9

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (‘Jan’)”;
 statement.executeUpdate(sql);


User

name : varchar(3)
 NOT-NULL,
 UNIQUE

What happens? Assuming the user table is empty

slide-10
SLIDE 10

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (‘Jan’)”;
 statement.executeUpdate(sql);


User

name : varchar(3)
 NOT-NULL,
 UNIQUE

1 row updated

slide-11
SLIDE 11

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (‘Jan’)”;
 statement.executeUpdate(sql);
 statement.executeUpdate(sql);

What will happen?

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

What happens?

slide-12
SLIDE 12

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (‘Jan’)”;
 statement.executeUpdate(sql);
 statement.executeUpdate(sql);

What will happen?

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

SQLIntegrityConstraint
 ViolationException

slide-13
SLIDE 13

Applicatio n JDBC Database

executeUpdate(sql) INSERT return 1 Inserted 1 executeUpdate(sql) INSERT Unique violation throw

SQLIntegrityConstraint
 ViolationException

slide-14
SLIDE 14

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (‘Jan’)”;
 statement.executeUpdate(sql);
 statement.executeUpdate(sql);

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

slide-15
SLIDE 15

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (‘Jan’)”;
 try {
 statement.executeUpdate(sql);
 statement.executeUpdate(sql);
 } catch (SQLIntegrityConstraintViolationException e) {
 throw new RuntimeException(“Name already exists”);
 }

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

slide-16
SLIDE 16

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (NULL)”;
 statement.executeUpdate(sql);

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

What happens?

slide-17
SLIDE 17

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (NULL)”;
 statement.executeUpdate(sql);

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

SQLIntegrityConstraint
 ViolationException

slide-18
SLIDE 18

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (NULL)”;
 try {
 statement.executeUpdate(sql);
 } catch (SQLIntegrityConstraintViolationException e) {
 throw new RuntimeException(“Name is required”);
 throw new RuntimeException(“Name already exists”);
 }

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

slide-19
SLIDE 19

Unique key violation

SQLIntegrityConstraint
 ViolationException

Not null violation

slide-20
SLIDE 20

Unique key violation Not null violation

SQLIntegrityConstraint
 ViolationException Which was violated?

slide-21
SLIDE 21

SQLException

+ getSQLState() : int + getMessage() : String

SQLIntegrityConstraint
 ViolationException

slide-22
SLIDE 22

SQLException

+ getSQLState() : int + getMessage() : String

SQLIntegrityConstraint
 ViolationException

slide-23
SLIDE 23

State Name 23000 Integrity constraint 23001 Restrict violation 23502 Not null violation 23503 Foreign key violation 23505 Unique violation 23514 Check violation

slide-24
SLIDE 24

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (NULL)”;
 try {
 statement.executeUpdate(sql);
 } catch (SQLIntegrityConstraintViolationException e) {
 if (e.getSQLState() == 23502) {
 throw new RuntimeException(“Name is required”);
 } else if (e.getSQLState() == 23505) {
 throw new RuntimeException(“Name already exists”);
 }
 }

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

slide-25
SLIDE 25

Connection connection = …;
 Statement statement = connection.createStatement(); String sql = “INSERT INTO user (name) VALUES (NULL)”;
 try {
 statement.executeUpdate(sql);
 } catch (SQLIntegrityConstraintViolationException e) {
 if (e.getSQLState() == 23502) {
 throw new RuntimeException(“Name is required”);
 } else if (e.getSQLState() == 23505) {
 throw new RuntimeException(“Name already exists”);
 }
 }

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

Complicated Boilerplate Assumptions

slide-26
SLIDE 26

Multiple not-null values

User

name : varchar(3)
 NOT-NULL,
 UNIQUE email : varchar(30)
 NOT-NULL, 
 UNIQUE

slide-27
SLIDE 27

uk_user_name uk_user_email Multiple not-null values Multiple unique values

User

name : varchar(3)
 NOT-NULL,
 UNIQUE email : varchar(30)
 NOT-NULL, 
 UNIQUE

Which was violated?

slide-28
SLIDE 28

SQLException

+ getSQLState() : int + getMessage() : String

SQLIntegrityConstraint
 ViolationException

slide-29
SLIDE 29

Vendor messages

HSQL

integrity constraint violation: unique constraint or index violation;
 UK_USER_NAME table: USER

PostgreSQL

ERROR: duplicate key value violates unique constraint \"uk_user_name\"
 Detail: Key (name)=(Jan) already exists.

Oracle

ORA-00001: unique constraint (GOTO.UK_USER_NAME) violated\n

MySQL

Duplicate entry 'Jan' for key 'uk_user_name'

H2

Unique index or primary key violation: "UK_USER_NAME_INDEX_1 ON GOTO.USER(NAME)";
 SQL statement:\ninsert into user (name) values (?) [23505-171]

They are all difgerent

slide-30
SLIDE 30

Vendor messages

HSQL

integrity constraint violation: unique constraint or index violation;
 UK_USER_NAME table: USER

PostgreSQL

ERROR: duplicate key value violates unique constraint \"uk_user_name\"
 Detail: Key (name)=(Jan) already exists.

Oracle

ORA-00001: unique constraint (GOTO.UK_USER_NAME) violated\n

MySQL

Duplicate entry 'Jan' for key 'uk_user_name'

H2

Unique index or primary key violation: "UK_USER_NAME_INDEX_1 ON GOTO.USER(NAME)";
 SQL statement:\ninsert into user (name) values (?) [23505-171]

The info is there

slide-31
SLIDE 31
  • Message
  • Pattern matching
  • Vendor specific

Extract violation info

Just too diffjcult Focus on application logic

slide-32
SLIDE 32

JDBC needs a better exception API


( for integrity constraints )

Concrete exception classes

UniqueKeyViolationException NotNullViolationException

Access to constraint info

getColumnName() getConstraintName()

slide-33
SLIDE 33

Workaround

slide-34
SLIDE 34

Prevent violations

slide-35
SLIDE 35
  • Data integrity checks in application layer.

Prevent violations

slide-36
SLIDE 36

Prevent not-null

if (user.getName() == null) {
 throw new RuntimeException(“Name is required”);
 }

slide-37
SLIDE 37

Javax validation

public class User {
 @NotNull
 private String name;
 }

Conveys No SQL exception Less database interaction

slide-38
SLIDE 38

Applicatio n Database throw new RuntimeException

Less interaction

slide-39
SLIDE 39

User

@NotNull
 private String name

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

Application Database

Duplication

slide-40
SLIDE 40

User

@NotNull
 private String name

User

name : varchar(3)
 NOT-NULL,
 UNIQUE

Application Database

Kept in sync Unexpected SQL exceptions

Duplication

slide-41
SLIDE 41
  • Complicated
  • Depends on other rows

Prevent unique violation

slide-42
SLIDE 42

id name NULL

Testable in isolation

slide-43
SLIDE 43

id name Jan

slide-44
SLIDE 44

id name id name

users

Piet Henk Jan 1 2 3 Jan

Requires data

slide-45
SLIDE 45

No SQL exceptions Extra query Not atomic

if (countUsersWithName(user.getName()) > 0) {
 throw new RuntimeException(“Name already exists”);
 } private int countUsersWithName(String name) {
 return jdbcTemplate.queryForObject(
 “SELECT COUNT(1) FROM user where name = ?”,
 name, Long.class);
 }

slide-46
SLIDE 46

Problem: Not atomic

Applicatio n Database

INSERT (name) VALUES (‘Jan’) INSERTED 1

Thread 2

COUNT WHERE name = ‘Jan’ return 0

Thread 1

INSERT (name) VALUES (‘Jan’) Unique key violation

Thread 1

Decision on


  • ld data

Unexpected Uncaught

slide-47
SLIDE 47

Recap

No SQL exceptions Error prone Duplication

Not null

Extra query

Unique key

No SQL exceptions Error prone

Lack proper solution

slide-48
SLIDE 48

Solution

Java Repository Bridge - JaRB

slide-49
SLIDE 49

Databases are good at
 maintaining integrity; 


let them!

slide-50
SLIDE 50

Catch exception Prevent exception

Testable in isolation

Not null Type Length Check Unique key Foreign key Primary key

slide-51
SLIDE 51

Prevent exception

Validation

Not null Type Length

slide-52
SLIDE 52

@Entity


@DatabaseConstrained


public class User {
 @NotNull @Length(max=3)
 private String name;
 private String email;
 }

User

name : varchar(3)
 NOT-NULL,
 UNIQUE
 email : varchar(100)

No duplication Retrieve constraints
 Database as

  • nly truth
slide-53
SLIDE 53

validate(new User(‘Henk’));

varchar(3)
 not null

name = ‘Henk’
 email = null

  • 1. Loop over properties
  • 3. Check name ‘Henk’

  • n metadata

Database Application

  • 2. Get metadata


user.name

Determine column name (Hibernate)

slide-54
SLIDE 54

Database Application

name = ‘Henk’
 email = null

varchar(3)
 not null

  • 1. Loop over properties
  • 3. Check name ‘Henk’

  • n metadata

“ Name cannot be longer
 than 3 characters “

  • 2. Get metadata


user.name

validate(new User(‘Henk’));

slide-55
SLIDE 55

validate(new User(‘Henk’));

name = null
 email = null

  • 1. Loop over properties
  • 3. Check null name

  • n metadata

validate(new User(null));

varchar(3)
 not null

Database Application

  • 2. Get metadata


user.name

slide-56
SLIDE 56

validate(new User(‘Henk’));

Application

name = null
 email = null

varchar(3)
 not null

  • 1. Loop over properties
  • 3. Check null name

  • n metadata

validate(new User(null));

“ Name cannot be null “

Database

  • 2. Get metadata


user.name

slide-57
SLIDE 57

validate(new User(‘Henk’));

name = ‘Jan’
 email = null

  • 1. Loop over properties
  • 3. Check name ‘Jan’

  • n metadata

validate(new User(null)); validate(new User(‘Jan’));

Database Application

varchar(3)
 not null

  • 2. Get metadata


user.name

slide-58
SLIDE 58

validate(new User(‘Henk’));

Database Application

name = ‘Jan’
 email = null

varchar(3)
 not null

  • 1. Loop over properties
  • 3. Check name ‘Jan’

  • n metadata
  • 2. Get metadata


user.name

validate(new User(null)); validate(new User(‘Jan’));

slide-59
SLIDE 59

validate(new User(‘Henk’));

name = ‘Jan’
 email = null

varchar(100)

  • 1. Loop over properties
  • 3. Check null email

  • n metadata

Database Application

  • 2. Get metadata


user.email

validate(new User(null)); validate(new User(‘Jan’));

slide-60
SLIDE 60

validate(new User(‘Henk’));

Database Application

name = ‘Jan’
 email = null

varchar(100)

  • 1. Loop over properties
  • 3. Check null email

  • n metadata
  • 2. Get metadata


user.email

validate(new User(null)); validate(new User(‘Jan’));

slide-61
SLIDE 61

validate(new User(‘Henk’));

Database Application

name = ‘Jan’
 email = null

varchar(100)

  • 1. Loop over properties
  • 3. Check null email

  • n metadata
  • 2. Get metadata


user.email

validate(new User(null)); validate(new User(‘Jan’));

slide-62
SLIDE 62

Super class

@MappedSuperclass


@DatabaseConstrained


public abstract class BaseEntity {
 } @Entity
 public class User extends BaseEntity {
 private String name;
 private String email;
 }

slide-63
SLIDE 63

JDBC

@DatabaseConstrained


public class User {
 private String name;
 private String email;
 }

Custom schema mapper

slide-64
SLIDE 64

Catch exception

Exception translation

Check Unique key Foreign key Primary key

slide-65
SLIDE 65

Translate the JDBC exception into a proper constraint exception

slide-66
SLIDE 66

Existing translators

slide-67
SLIDE 67
  • Object Relation Mapping
  • Extracts constraint name from message

Hibernate

slide-68
SLIDE 68

Hibernate

ConstraintViolationException getConstraintName()

Access to constraint name Hardcoded names Heavy for plain JDBC

slide-69
SLIDE 69

Hardcoded names

try {
 // Insert user
 } catch (ConstraintViolationException e) {
 if (e.getConstraintName() == “uk_user_name”) {
 // Handle error
 }
 }

Too technical Focus on domain

slide-70
SLIDE 70
  • Dependency Injection
  • Templates
  • JDBC
  • DAO

Spring

slide-71
SLIDE 71
  • JdbcTemplate
  • SQLExceptionTranslator
  • Error codes
  • Register own classes
  • No constraint name

Spring JDBC

slide-72
SLIDE 72

Spring

DataAccessException DataIntegrityViolationException

Consistent hierarchy Extensible

slide-73
SLIDE 73
  • ORM (e.g. Hibernate)
  • PersistenceExceptionTranslator
  • Proxy

Spring DAO

slide-74
SLIDE 74


 
 Spring$Proxy PersistenceExceptionTranslator

ConstraintViolation
 Exception JPASystemException

UserRepository

slide-75
SLIDE 75

No constraint name

ConstraintViolationException getConstraintName() JPASystemException DataAccessException

cause

Hierarchy Weaker API

slide-76
SLIDE 76

Weaker API

try {
 userRepository.save(user);
 } catch (JPASystemException e) {
 ConstraintViolationException ce = 
 (ConstraintViolationException) e.getCause();
 if (ce.getConstraintName() == “uk_user_name”) {
 // Handle error
 }
 } Unsafe cast

Why isn’t this easier?

slide-77
SLIDE 77

Recap

Constraint name Hierarchy Extensible Hibernate Spring JaRB Best of both worlds

slide-78
SLIDE 78

JaRB

Concrete and domain specific exceptions.

Map each constraint to a custom exception.

slide-79
SLIDE 79

try {
 userRepository.save(new User(“Jan”));
 } catch (UserNameAlreadyExistsException e) {
 error(“User name already exists.”);
 }

slide-80
SLIDE 80

try {
 userRepository.save(new User(“Jan”));
 } catch (UserNameAlreadyExistsException e) {
 error(“User name already exists.”);
 } catch (UserEmailAlreadyExistsException e) {
 error(“User email already exists.”);
 }

slide-81
SLIDE 81

Translator

SQLIntegrity
 ConstraintException UserNameAlready
 ExistsException

slide-82
SLIDE 82

Resolver

ERROR: duplicate key value violates
 unique constraint \"uk_user_name\"
 Detail: Key (name)=(Jan) already exists.

Extract all information from exception

SQLIntegrity
 ConstraintException

slide-83
SLIDE 83

Resolver

ERROR: duplicate key value violates
 unique constraint \"uk_user_name\"
 Detail: Key (name)=(Jan) already exists.

Vendor specific Pattern matching

Extract all information from exception

Column name Value Constraint name

Version specific

SQLIntegrity
 ConstraintException

slide-84
SLIDE 84

Resolvers

  • Pattern matching (default)
  • PostgreSQL
  • Oracle
  • MySQL
  • HSQL
  • H2
  • Hibernate: constraint name only
slide-85
SLIDE 85

Factory

Create a concrete exception

slide-86
SLIDE 86

Default factory

UniqueKeyViolationException NotNullViolationException LengthExceededViolationExceptio n PrimaryKeyViolationException CheckFailedException InvalidTypeException ForeignKeyViolationException

slide-87
SLIDE 87

DatabaseConstraintViolationException

UniqueKeyViolationException NotNullViolationException LengthExceededViolationExceptio n PrimaryKeyViolationException CheckFailedException InvalidTypeException ForeignKeyViolationException UserNameAlreadyExistsException

Constraint info

slide-88
SLIDE 88

Custom exceptions

@NamedConstraint(“uk_user_name”)
 public class UserNameAlreadyExistsException
 extends UniqueKeyViolationException {
 }

Scanned from class path Registered on constraint

slide-89
SLIDE 89

Custom exceptions

uk_user_name

UserNameAlreadyExistsException

uk_user_email

UniqueKeyViolationException

slide-90
SLIDE 90

Injectable arguments

@NamedConstraint(“uk_user_name”)
 public class UserNameAlreadyExistsException
 extends UniqueKeyViolationException {
 
 UserNameAlreadyExistsException(…) { }
 
 }

DatabaseConstraintViolation Throwable (cause) ExceptionFactory

slide-91
SLIDE 91

Less concrete

try {
 userRepository.save(new User(“Jan”));
 } catch (UniqueKeyViolationException e) {
 error(“User name already exists.”);
 }

slide-92
SLIDE 92

How?

slide-93
SLIDE 93

Enable in Spring

@EnableDatabaseConstraints(basePackage = “org.myproject”)

Enable exception translation Enable database validation Resolve database vendor Register custom exceptions

<jarb:enable-constraints base-package=“org.myproject”/>

slide-94
SLIDE 94

Get source

<dependency> <groupId>org.jarbframework</groupId> <artifactId>jarb-constraints</artifactId> <version>2.1.0</version> </dependency>

Maven central

http://www.jarbframework.org

Github

slide-95
SLIDE 95

Questions?