Skip to content

Commit 7c0107c

Browse files
author
bwajtr
committed
Jooq and JDBCTemplate implementation of scenario ten - call stored procedure or function.
Function itself is implemented in register_employee.sql
1 parent 8ed3a51 commit 7c0107c

19 files changed

Lines changed: 431 additions & 78 deletions

dbscenarios.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
7. Fetch entity and it's one-to-many relation (Departments for Company)
88
8. Update entities one-to-many relation - add two items, update two items and delete one item
99
9. Complex select - construct select where conditions based on some boolean conditions + throw in some joins
10-
10. Call stored procedure
10+
10. Call stored procedure/function and process results
1111
11. Optimistic locking

jooq.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// You can invoke JOOQ code generation using this script.
2-
// Just run it from IDE, or use "gradle --rerun-tasks -b jooq.gradle"
2+
// Just run it from IDE, or to force code generation use "gradle --rerun-tasks -b jooq.gradle"
33

44
buildscript {
55
repositories {
File renamed without changes.

sql-updates/create-script.sql

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
-- Meant for PostgreSQL 9.5 --
2+
-- DO NOT FORGET TO EXECUTE ALSO sql_functions/register_employee.sql
3+
24

35
-- (re)create tables first
46

@@ -10,7 +12,7 @@ DROP TABLE Company;
1012

1113
CREATE TABLE Company (
1214
pid SERIAL PRIMARY KEY,
13-
name TEXT NOT NULL,
15+
name TEXT NOT NULL UNIQUE,
1416
address TEXT
1517
);
1618

@@ -21,14 +23,16 @@ CREATE TABLE Department (
2123
);
2224

2325
CREATE TABLE Employee (
24-
pid SERIAL PRIMARY KEY,
26+
pid SERIAL PRIMARY KEY,
2527
department_pid INTEGER NOT NULL REFERENCES Department (pid) ON DELETE CASCADE,
26-
name TEXT NOT NULL,
27-
surname TEXT NOT NULL,
28-
email TEXT,
29-
salary numeric(10,2) CHECK (salary > 0)
28+
name TEXT NOT NULL,
29+
surname TEXT NOT NULL,
30+
email TEXT,
31+
salary NUMERIC(10, 2) CHECK (salary > 0),
32+
CONSTRAINT emloyee_name_surname_unique UNIQUE (name, surname)
3033
);
3134

35+
3236
CREATE TABLE Project (
3337
pid SERIAL PRIMARY KEY,
3438
name TEXT NOT NULL,
@@ -88,3 +92,4 @@ INSERT INTO ProjectEmployee (project_pid, employee_pid) VALUES (1, 9);
8892
INSERT INTO ProjectEmployee (project_pid, employee_pid) VALUES (2, 10);
8993
INSERT INTO ProjectEmployee (project_pid, employee_pid) VALUES (2, 5);
9094
INSERT INTO ProjectEmployee (project_pid, employee_pid) VALUES (1, 4);
95+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
-- Procedure which allows to register new employee and assign it to department and company.
2+
-- Reason why it's in stored procedure is, that we want to allow to register the employee even if
3+
-- the department and/or company does not exist in DB yet - in such case we would create such
4+
-- company/department records along with the employee record. It would mean to do lot of DB
5+
-- roundtrips if we want do such thing in java code.
6+
7+
CREATE OR REPLACE FUNCTION register_employee(_name TEXT, _surname TEXT, _email TEXT,
8+
_salary NUMERIC(10, 2), _department_name TEXT, _company_name TEXT)
9+
returns TABLE (
10+
employee_id integer,
11+
department_id integer,
12+
company_id integer
13+
)
14+
AS
15+
$$
16+
DECLARE
17+
_employee_id integer;
18+
_department_id integer;
19+
_company_id integer;
20+
BEGIN
21+
-- check if company exists; create it if it doesn't
22+
select pid into _company_id from company where company.name = _company_name;
23+
IF (_company_id is null) THEN
24+
INSERT INTO company (name)
25+
values (_company_name) RETURNING PID INTO _company_id;
26+
raise notice 'Created new company with pid %', _company_id;
27+
ELSE
28+
raise notice 'Found existing company with pid %', _company_id;
29+
END IF;
30+
31+
-- check if department exists; create it if it doesn't
32+
select pid into _department_id from department where department.name = _department_name and department.company_pid = _company_id;
33+
IF (_department_id is null) THEN
34+
INSERT INTO department (company_pid, name)
35+
values (_company_id, _department_name) RETURNING PID INTO _department_id;
36+
raise notice 'Created new department with pid %', _department_id;
37+
ELSE
38+
raise notice 'Found existing department with pid %', _department_id;
39+
END IF;
40+
41+
-- now we have company and department, finally insert the employee record
42+
INSERT INTO employee (department_pid, name, surname, email, salary) VALUES
43+
(_department_id, _name, _surname, _email, _salary) RETURNING PID into _employee_id;
44+
raise notice 'Inserted new employee record with PID %', _employee_id;
45+
46+
--return out what happened here with relevant data
47+
return query
48+
select _employee_id, _department_id, _company_id;
49+
50+
END
51+
$$ LANGUAGE plpgsql;

src/main/java/com/clevergang/dbtests/Scenarios.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,19 @@ private List<Department> createNewDepartmentsList(Company company) {
213213
public void scenarioNine() {
214214
List<ProjectsWithCostsGreaterThanOutput> projectsWithCostsGreaterThan = repository.getProjectsWithCostsGreaterThan(70000);
215215

216-
logger.info("scenarioNine output: " + projectsWithCostsGreaterThan);
216+
logger.info("scenarioNine output: {}", projectsWithCostsGreaterThan);
217217
}
218+
219+
/**
220+
* Execute stored procedure/function and process results
221+
*
222+
*/
223+
public void scenarioTen() {
224+
RegisterEmployeeOutput output = repository.callRegisterEmployee("Bretislav", "Wajtr", "bretislav.wajtr@test.com", new BigDecimal(40000), "MyDepartment", "MyCompany");
225+
226+
logger.info("scenarioTen output: {}", output);
227+
}
228+
218229
}
219230

220231

src/main/java/com/clevergang/dbtests/repository/api/DataRepository.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.clevergang.dbtests.repository.api.data.*;
44

5+
import java.math.BigDecimal;
56
import java.util.List;
67

78
/**
@@ -35,11 +36,14 @@ public interface DataRepository {
3536

3637
List<Integer> insertProjects(List<Project> projects);
3738

39+
List<ProjectsWithCostsGreaterThanOutput> getProjectsWithCostsGreaterThan(int totalCostBoundary);
40+
3841
// employees
3942

4043
List<Employee> employeesWithSalaryGreaterThan(Integer minSalary);
4144

4245
void updateEmployee(Employee employeeToUpdate);
4346

44-
List<ProjectsWithCostsGreaterThanOutput> getProjectsWithCostsGreaterThan(int totalCostBoundary);
47+
RegisterEmployeeOutput callRegisterEmployee(String name, String surname, String email, BigDecimal salary,
48+
String departmentName, String companyName);
4549
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.clevergang.dbtests.repository.api.data;
2+
3+
import java.util.Objects;
4+
5+
/**
6+
* @author Bretislav Wajtr <bretislav.wajtr@ibacz.eu>
7+
*/
8+
public class RegisterEmployeeOutput {
9+
private Integer companyPid;
10+
private Integer departmentPid;
11+
private Integer employeePid;
12+
13+
public Integer getCompanyPid() {
14+
return companyPid;
15+
}
16+
17+
public void setCompanyPid(Integer companyPid) {
18+
this.companyPid = companyPid;
19+
}
20+
21+
public Integer getDepartmentPid() {
22+
return departmentPid;
23+
}
24+
25+
public void setDepartmentPid(Integer departmentPid) {
26+
this.departmentPid = departmentPid;
27+
}
28+
29+
public Integer getEmployeePid() {
30+
return employeePid;
31+
}
32+
33+
public void setEmployeePid(Integer employeePid) {
34+
this.employeePid = employeePid;
35+
}
36+
37+
@Override
38+
public boolean equals(Object o) {
39+
if (this == o) return true;
40+
if (o == null || getClass() != o.getClass()) return false;
41+
RegisterEmployeeOutput that = (RegisterEmployeeOutput) o;
42+
return Objects.equals(companyPid, that.companyPid) &&
43+
Objects.equals(departmentPid, that.departmentPid) &&
44+
Objects.equals(employeePid, that.employeePid);
45+
}
46+
47+
@Override
48+
public int hashCode() {
49+
return Objects.hash(companyPid, departmentPid, employeePid);
50+
}
51+
52+
@Override
53+
public String toString() {
54+
return "RegisterEmployeeOutput{" +
55+
"companyPid=" + companyPid +
56+
", departmentPid=" + departmentPid +
57+
", employeePid=" + employeePid +
58+
'}';
59+
}
60+
}

src/main/java/com/clevergang/dbtests/repository/impl/jdbctemplate/JDBCDataRepositoryImpl.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.springframework.jdbc.support.KeyHolder;
1414
import org.springframework.stereotype.Repository;
1515

16+
import java.math.BigDecimal;
1617
import java.sql.Date;
1718
import java.util.HashMap;
1819
import java.util.List;
@@ -185,6 +186,37 @@ public List<ProjectsWithCostsGreaterThanOutput> getProjectsWithCostsGreaterThan(
185186
return jdbcTemplate.query(query, params, mapper);
186187
}
187188

189+
@Override
190+
public RegisterEmployeeOutput callRegisterEmployee(String name, String surname, String email, BigDecimal salary, String departmentName, String companyName) {
191+
String query;
192+
query = "SELECT employee_id, department_id, company_id FROM register_employee(\n" +
193+
" _name := :name, \n" +
194+
" _surname := :surname, \n" +
195+
" _email := :email, \n" +
196+
" _salary := :salary, \n" +
197+
" _department_name := :departmentName, \n" +
198+
" _company_name := :companyName\n" +
199+
")";
200+
201+
Map<String, Object> params = new HashMap<>();
202+
params.put("name", name);
203+
params.put("surname", surname);
204+
params.put("email", email);
205+
params.put("salary", salary);
206+
params.put("departmentName", departmentName);
207+
params.put("companyName", companyName);
208+
209+
RowMapper<RegisterEmployeeOutput> mapper = (rs, rowNum) -> {
210+
RegisterEmployeeOutput row = new RegisterEmployeeOutput();
211+
row.setEmployeePid(rs.getInt("employee_id"));
212+
row.setDepartmentPid(rs.getInt("department_id"));
213+
row.setCompanyPid(rs.getInt("company_id"));
214+
return row;
215+
};
216+
217+
return jdbcTemplate.queryForObject(query, params, mapper);
218+
}
219+
188220
@Override
189221
public List<Department> findDepartmentsOfCompany(Company company) {
190222
String query = "SELECT pid, company_pid, name" +

src/main/java/com/clevergang/dbtests/repository/impl/jooq/JooqDataRepositoryImpl.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.clevergang.dbtests.repository.api.DataRepository;
44
import com.clevergang.dbtests.repository.api.data.*;
5+
import com.clevergang.dbtests.repository.impl.jooq.generated.Routines;
6+
import com.clevergang.dbtests.repository.impl.jooq.generated.routines.RegisterEmployee;
57
import com.clevergang.dbtests.repository.impl.jooq.generated.tables.records.DepartmentRecord;
68
import com.clevergang.dbtests.repository.impl.jooq.generated.tables.records.EmployeeRecord;
79
import org.jooq.*;
@@ -147,6 +149,20 @@ public List<ProjectsWithCostsGreaterThanOutput> getProjectsWithCostsGreaterThan(
147149
return getProjectsWithCostsUsingNativeQuery(totalCostBoundary);
148150
}
149151

152+
@Override
153+
public RegisterEmployeeOutput callRegisterEmployee(String name, String surname, String email, BigDecimal salary, String departmentName, String companyName) {
154+
// very easy way how to call a stored procedure. Notice that RegisterEmployee class is generated by jooq...
155+
RegisterEmployeeOutput result = new RegisterEmployeeOutput();
156+
157+
RegisterEmployee output = Routines.registerEmployee(create.configuration(), name, surname, email, salary, departmentName, companyName);
158+
159+
result.setCompanyPid(output.getCompanyId());
160+
result.setDepartmentPid(output.getDepartmentId());
161+
result.setEmployeePid(output.getEmployeeId());
162+
163+
return result;
164+
}
165+
150166
private List<ProjectsWithCostsGreaterThanOutput> getProjectsWithCostsUsingNativeQuery(int totalCostBoundary) {
151167
// FIXME: We should cache loaded scripts in real production code!
152168
String query = loadSQLScript("queries/getProjectsWithCostsUsingNativeQuery.sql");

0 commit comments

Comments
 (0)