-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathUpdateStatement.java
153 lines (136 loc) · 5 KB
/
UpdateStatement.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
* Made with all the love in the world
* by scireum in Remshalden, Germany
*
* Copyright by scireum GmbH
* http://www.scireum.de - [email protected]
*/
package sirius.db.jdbc;
import sirius.db.mixing.EntityDescriptor;
import sirius.db.mixing.Mapping;
import sirius.kernel.commons.Monoflop;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* Provides a simple helper to generate UPDATE statements with multiple where conditions on {@link SQLEntity entities}.
* <p>
* <b>Note that this will not execute any {@link sirius.db.mixing.annotations.BeforeSave} or
* {@link sirius.db.mixing.annotations.AfterSave} handlers.</b>
* <p>
* This can be used to generate queries like {@code UPDATE table SET x = 'Value' WHERE y = 'Value'} without having
* to worry about typing field and table names correctly. Such update are sometimes more efficient as a lot of the
* framework overhead is reduced (this is essentially just a builder which generates a {@link PreparedStatement}).
* This can also be used for conditional updates (e.g. optimistic locking algorithms and the like.
* <p>
* Note that this, being a builder class with minimal state overhead, does not support to add another update
* via {@link #set(Mapping, Object)} once the first constraint has been added via {@link #where(Mapping, Object)}.
*/
public class UpdateStatement extends GeneratedStatement<UpdateStatement> {
private static final String MICROTIMING_KEY = "UPDATE";
private final Monoflop setPartStarted = Monoflop.create();
protected UpdateStatement(EntityDescriptor descriptor, Database db) {
super(descriptor, db);
}
@Override
protected String microtimingKey() {
return MICROTIMING_KEY;
}
/**
* Specifies a field to update with the given value.
*
* @param field the field to update
* @param value the value to place in the field
* @return the query itself for fluent method calls
*/
public UpdateStatement set(Mapping field, Object value) {
prepareSet();
append(determineEffectiveColumnName(field));
append(" = ?");
parameters.add(value);
return this;
}
private void prepareSet() {
if (wherePartStarted.isToggled()) {
throw new IllegalStateException("Cannot append to the SET part when already building the WHERE part");
}
if (setPartStarted.firstCall()) {
append("UPDATE ");
append(descriptor.getRelationName());
append(" SET ");
} else {
append(", ");
}
}
/**
* Increments the given field by one.
* <p>
* This is an atomic operation as it is done via a generate SQL expression.
*
* @param field the field to increment
* @return the query itself for fluent method calls
*/
public UpdateStatement inc(Mapping field) {
prepareSet();
String columnName = determineEffectiveColumnName(field);
append(columnName);
append(" = ");
append(columnName);
append(" + 1");
return this;
}
/**
* Specifies a field to update with the given value if a given condition is filled.
* <p>
* This is a boilerplate method which can simplify some code fragments as some updates depend on checks or
* parameters.
*
* @param field the field to update
* @param value the value to place in the field
* @param condition the condition which needs to be fulfilled (<tt>true</tt>) in order to actually put the value
* in the field.
* @return the query itself for fluent method calls
*/
public UpdateStatement setIf(Mapping field, Object value, boolean condition) {
if (condition) {
set(field, value);
}
return this;
}
/**
* Specifies a field to update with the given value (as long as it isn't <tt>null</tt>).
*
* @param field the field to update
* @param value the value to place in the field
* @return the query itself for fluent method calls
*/
public UpdateStatement setIgnoringNull(Mapping field, Object value) {
return setIf(field, value, value != null);
}
/**
* Specifies a field to be filled with the current {@link LocalDateTime}.
*
* @param field the field to update
* @return the query itself for fluent method calls
*/
public UpdateStatement setToNow(Mapping field) {
return set(field, LocalDateTime.now());
}
/**
* Specifies a field to be filled with the current {@link LocalDate}.
*
* @param field the field to update
* @return the query itself for fluent method calls
*/
public UpdateStatement setToToday(Mapping field) {
return set(field, LocalDate.now());
}
@Override
public int executeUpdate() throws SQLException {
if (!setPartStarted.isToggled()) {
return 0;
}
return super.executeUpdate();
}
}