@@ -12,20 +12,20 @@ tags:
12
12
13
13
## Intent
14
14
15
- When a business transaction is completed, all the the updates are sent as one big unit of work to be
15
+ When a business transaction is completed, all the updates are sent as one big unit of work to be
16
16
persisted in one go to minimize database round-trips.
17
17
18
18
## Explanation
19
19
20
- Real world example
20
+ Real- world example
21
21
22
- > We have a database containing student information. Administrators all over the country are
23
- > constantly updating this information and it causes high load on the database server. To make the
22
+ > Arms dealer has a database containing weapon information. Merchants all over the town are
23
+ > constantly updating this information and it causes a high load on the database server. To make the
24
24
> load more manageable we apply to Unit of Work pattern to send many small updates in batches.
25
25
26
26
In plain words
27
27
28
- > Unit of Work merges many small database updates in single batch to optimize the number of
28
+ > Unit of Work merges many small database updates in a single batch to optimize the number of
29
29
> round-trips.
30
30
31
31
[ MartinFowler.com] ( https://martinfowler.com/eaaCatalog/unitOfWork.html ) says
@@ -35,37 +35,20 @@ In plain words
35
35
36
36
** Programmatic Example**
37
37
38
- Here's the ` Student ` entity that is being persisted to the database.
38
+ Here's the ` Weapon ` entity that is being persisted in the database.
39
39
40
40
``` java
41
- public class Student {
42
- private final Integer id;
43
- private final String name;
44
- private final String address;
45
-
46
- public Student (Integer id , String name , String address ) {
47
- this . id = id;
48
- this . name = name;
49
- this . address = address;
50
- }
51
-
52
- public String getName () {
53
- return name;
54
- }
55
-
56
- public Integer getId () {
57
- return id;
58
- }
59
-
60
- public String getAddress () {
61
- return address;
62
- }
41
+ @Getter
42
+ @RequiredArgsConstructor
43
+ public class Weapon {
44
+ private final Integer id;
45
+ private final String name;
63
46
}
64
47
```
65
48
66
- The essence of the implementation is the ` StudentRepository ` implementing the Unit of Work pattern.
49
+ The essence of the implementation is the ` ArmsDealer ` implementing the Unit of Work pattern.
67
50
It maintains a map of database operations (` context ` ) that need to be done and when ` commit ` is
68
- called it applies them in single batch.
51
+ called it applies them in a single batch.
69
52
70
53
``` java
71
54
public interface IUnitOfWork <T> {
@@ -84,96 +67,117 @@ public interface IUnitOfWork<T> {
84
67
}
85
68
86
69
@Slf4j
87
- public class StudentRepository implements IUnitOfWork<Student > {
88
-
89
- private final Map<String , List<Student > > context;
90
- private final StudentDatabase studentDatabase;
91
-
92
- public StudentRepository (Map<String , List<Student > > context , StudentDatabase studentDatabase ) {
93
- this . context = context;
94
- this . studentDatabase = studentDatabase;
95
- }
96
-
97
- @Override
98
- public void registerNew (Student student ) {
99
- LOGGER . info(" Registering {} for insert in context." , student. getName());
100
- register(student, IUnitOfWork . INSERT );
101
- }
102
-
103
- @Override
104
- public void registerModified (Student student ) {
105
- LOGGER . info(" Registering {} for modify in context." , student. getName());
106
- register(student, IUnitOfWork . MODIFY );
107
-
108
- }
109
-
110
- @Override
111
- public void registerDeleted (Student student ) {
112
- LOGGER . info(" Registering {} for delete in context." , student. getName());
113
- register(student, IUnitOfWork . DELETE );
114
- }
115
-
116
- private void register (Student student , String operation ) {
117
- var studentsToOperate = context. get(operation);
118
- if (studentsToOperate == null ) {
119
- studentsToOperate = new ArrayList<> ();
70
+ @RequiredArgsConstructor
71
+ public class ArmsDealer implements IUnitOfWork<Weapon > {
72
+
73
+ private final Map<String , List<Weapon > > context;
74
+ private final WeaponDatabase weaponDatabase;
75
+
76
+ @Override
77
+ public void registerNew (Weapon weapon ) {
78
+ LOGGER . info(" Registering {} for insert in context." , weapon. getName());
79
+ register(weapon, UnitActions . INSERT. getActionValue());
120
80
}
121
- studentsToOperate. add(student);
122
- context. put(operation, studentsToOperate);
123
- }
124
-
125
- @Override
126
- public void commit () {
127
- if (context == null || context. size() == 0 ) {
128
- return ;
81
+
82
+ @Override
83
+ public void registerModified (Weapon weapon ) {
84
+ LOGGER . info(" Registering {} for modify in context." , weapon. getName());
85
+ register(weapon, UnitActions . MODIFY. getActionValue());
86
+
129
87
}
130
- LOGGER . info(" Commit started" );
131
- if (context. containsKey(IUnitOfWork . INSERT )) {
132
- commitInsert();
88
+
89
+ @Override
90
+ public void registerDeleted (Weapon weapon ) {
91
+ LOGGER . info(" Registering {} for delete in context." , weapon. getName());
92
+ register(weapon, UnitActions . DELETE. getActionValue());
133
93
}
134
94
135
- if (context. containsKey(IUnitOfWork . MODIFY )) {
136
- commitModify();
95
+ private void register (Weapon weapon , String operation ) {
96
+ var weaponsToOperate = context. get(operation);
97
+ if (weaponsToOperate == null ) {
98
+ weaponsToOperate = new ArrayList<> ();
99
+ }
100
+ weaponsToOperate. add(weapon);
101
+ context. put(operation, weaponsToOperate);
137
102
}
138
- if (context. containsKey(IUnitOfWork . DELETE )) {
139
- commitDelete();
103
+
104
+ /**
105
+ * All UnitOfWork operations are batched and executed together on commit only.
106
+ */
107
+ @Override
108
+ public void commit () {
109
+ if (context == null || context. size() == 0 ) {
110
+ return ;
111
+ }
112
+ LOGGER . info(" Commit started" );
113
+ if (context. containsKey(UnitActions . INSERT. getActionValue())) {
114
+ commitInsert();
115
+ }
116
+
117
+ if (context. containsKey(UnitActions . MODIFY. getActionValue())) {
118
+ commitModify();
119
+ }
120
+ if (context. containsKey(UnitActions . DELETE. getActionValue())) {
121
+ commitDelete();
122
+ }
123
+ LOGGER . info(" Commit finished." );
140
124
}
141
- LOGGER . info(" Commit finished." );
142
- }
143
-
144
- private void commitInsert () {
145
- var studentsToBeInserted = context. get(IUnitOfWork . INSERT );
146
- for (var student : studentsToBeInserted) {
147
- LOGGER . info(" Saving {} to database." , student. getName());
148
- studentDatabase. insert(student);
125
+
126
+ private void commitInsert () {
127
+ var weaponsToBeInserted = context. get(UnitActions . INSERT. getActionValue());
128
+ for (var weapon : weaponsToBeInserted) {
129
+ LOGGER . info(" Inserting a new weapon {} to sales rack." , weapon. getName());
130
+ weaponDatabase. insert(weapon);
131
+ }
149
132
}
150
- }
151
133
152
- private void commitModify () {
153
- var modifiedStudents = context. get(IUnitOfWork . MODIFY );
154
- for (var student : modifiedStudents) {
155
- LOGGER . info(" Modifying {} to database." , student. getName());
156
- studentDatabase. modify(student);
134
+ private void commitModify () {
135
+ var modifiedWeapons = context. get(UnitActions . MODIFY. getActionValue());
136
+ for (var weapon : modifiedWeapons) {
137
+ LOGGER . info(" Scheduling {} for modification work." , weapon. getName());
138
+ weaponDatabase. modify(weapon);
139
+ }
157
140
}
158
- }
159
141
160
- private void commitDelete () {
161
- var deletedStudents = context. get(IUnitOfWork . DELETE );
162
- for (var student : deletedStudents) {
163
- LOGGER . info(" Deleting {} to database." , student. getName());
164
- studentDatabase. delete(student);
142
+ private void commitDelete () {
143
+ var deletedWeapons = context. get(UnitActions . DELETE. getActionValue());
144
+ for (var weapon : deletedWeapons) {
145
+ LOGGER . info(" Scrapping {}." , weapon. getName());
146
+ weaponDatabase. delete(weapon);
147
+ }
165
148
}
166
- }
167
149
}
168
150
```
169
151
170
- Finally, here's how we use the ` StudentRepository ` and ` commit ` the transaction .
152
+ Here is how the whole app is put together .
171
153
172
154
``` java
173
- studentRepository. registerNew(ram);
174
- studentRepository. registerModified(shyam);
175
- studentRepository. registerDeleted(gopi);
176
- studentRepository. commit();
155
+ // create some weapons
156
+ var enchantedHammer = new Weapon (1 , " enchanted hammer" );
157
+ var brokenGreatSword = new Weapon (2 , " broken great sword" );
158
+ var silverTrident = new Weapon (3 , " silver trident" );
159
+
160
+ // create repository
161
+ var weaponRepository = new ArmsDealer (new HashMap<String , List<Weapon > > (), new WeaponDatabase ());
162
+
163
+ // perform operations on the weapons
164
+ weaponRepository. registerNew(enchantedHammer);
165
+ weaponRepository. registerModified(silverTrident);
166
+ weaponRepository. registerDeleted(brokenGreatSword);
167
+ weaponRepository. commit();
168
+ ```
169
+
170
+ Here is the console output.
171
+
172
+ ```
173
+ 21:39:21.984 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Registering enchanted hammer for insert in context.
174
+ 21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Registering silver trident for modify in context.
175
+ 21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Registering broken great sword for delete in context.
176
+ 21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Commit started
177
+ 21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Inserting a new weapon enchanted hammer to sales rack.
178
+ 21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Scheduling silver trident for modification work.
179
+ 21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Scrapping broken great sword.
180
+ 21:39:21.989 [main] INFO com.iluwatar.unitofwork.ArmsDealer - Commit finished.
177
181
```
178
182
179
183
## Class diagram
@@ -186,7 +190,7 @@ Use the Unit Of Work pattern when
186
190
187
191
* To optimize the time taken for database transactions.
188
192
* To send changes to database as a unit of work which ensures atomicity of the transaction.
189
- * To reduce number of database calls.
193
+ * To reduce the number of database calls.
190
194
191
195
## Tutorials
192
196
0 commit comments