/
AddObjectOperation.java
133 lines (118 loc) · 5.67 KB
/
AddObjectOperation.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
/*
* Copyright (C) 2010-2021 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/
package com.evolveum.midpoint.repo.sqale.operations;
import java.util.Objects;
import java.util.UUID;
import com.querydsl.core.QueryException;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.repo.api.RepoAddOptions;
import com.evolveum.midpoint.repo.sqale.SqaleTransformerContext;
import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping;
import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject;
import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer;
import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject;
import com.evolveum.midpoint.repo.sqlbase.JdbcSession;
import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
public class AddObjectOperation<S extends ObjectType, Q extends QObject<R>, R extends MObject> {
private final PrismObject<S> object;
private final RepoAddOptions options;
private final OperationResult result;
private SqlRepoContext sqlRepoContext;
private Q root;
private ObjectSqlTransformer<S, Q, R> transformer;
public AddObjectOperation(@NotNull PrismObject<S> object,
@NotNull RepoAddOptions options, @NotNull OperationResult result) {
this.object = object;
this.options = options;
this.result = result;
}
/** Inserts the object provided to the constructor and returns its OID. */
public String execute(SqaleTransformerContext transformerContext)
throws SchemaException, ObjectAlreadyExistsException {
try {
// TODO utilize options and result
sqlRepoContext = transformerContext.sqlRepoContext();
SqaleTableMapping<S, Q, R> rootMapping =
sqlRepoContext.getMappingBySchemaType(object.getCompileTimeClass());
root = rootMapping.defaultAlias();
transformer = (ObjectSqlTransformer<S, Q, R>)
rootMapping.createTransformer(transformerContext);
if (object.getOid() == null) {
return addObjectWithoutOid();
} else if (options.isOverwrite()) {
return overwriteObject();
} else {
// OID is not null, but it's not overwrite either
return addObjectWithOid();
}
} catch (QueryException e) {
Throwable cause = e.getCause();
if (cause instanceof PSQLException) {
handlePostgresException((PSQLException) cause);
}
throw e;
}
}
private String overwriteObject() {
throw new UnsupportedOperationException(); // TODO
}
private String addObjectWithOid() throws SchemaException {
try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startTransaction()) {
R row = transformer.toRowObjectWithoutFullObject(object.asObjectable(), jdbcSession);
transformer.setFullObject(row, object.asObjectable());
UUID oid = jdbcSession.newInsert(root)
// default populate mapper ignores null, that's good, especially for objectType
.populate(row)
.executeWithKey(root.oid);
return Objects.requireNonNull(oid, "OID of inserted object can't be null")
.toString();
}
}
private String addObjectWithoutOid() throws SchemaException {
try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startTransaction()) {
R row = transformer.toRowObjectWithoutFullObject(object.asObjectable(), jdbcSession);
// first insert without full object, because we don't know the OID yet
UUID oid = jdbcSession.newInsert(root)
// default populate mapper ignores null, that's good, especially for objectType
.populate(row)
.executeWithKey(root.oid);
String oidString =
Objects.requireNonNull(oid, "OID of inserted object can't be null")
.toString();
object.setOid(oidString);
// now to update full object with known OID
transformer.setFullObject(row, object.asObjectable());
jdbcSession.newUpdate(root)
.set(root.fullObject, row.fullObject)
.where(root.oid.eq(oid))
.execute();
return oidString;
}
}
/** Throws more specific exception or returns and then original exception should be rethrown. */
private void handlePostgresException(PSQLException psqlException)
throws ObjectAlreadyExistsException {
String state = psqlException.getSQLState();
String message = psqlException.getMessage();
if (PSQLState.UNIQUE_VIOLATION.getState().equals(state)) {
if (message.contains("m_object_oid_pkey")) {
String oid = StringUtils.substringBetween(message, "(oid)=(", ")");
throw new ObjectAlreadyExistsException(
oid != null ? "Provided OID " + oid + " already exists" : message,
psqlException);
}
}
}
}