|
32 | 32 |
|
33 | 33 | import org.springframework.lang.Nullable;
|
34 | 34 | import org.springframework.util.Assert;
|
| 35 | +import org.springframework.util.ErrorHandler; |
35 | 36 |
|
36 | 37 | /**
|
37 | 38 | * The interface encapsulates the setting of query parameters which might use a significant number of variations of
|
|
43 | 44 | */
|
44 | 45 | interface QueryParameterSetter {
|
45 | 46 |
|
46 |
| - void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling); |
47 |
| - |
48 | 47 | /** Noop implementation */
|
49 |
| - QueryParameterSetter NOOP = (query, values, errorHandling) -> {}; |
| 48 | + QueryParameterSetter NOOP = (query, values, errorHandler) -> {}; |
| 49 | + |
| 50 | + /** |
| 51 | + * Creates a new {@link QueryParameterSetter} for the given value extractor, JPA parameter and potentially the |
| 52 | + * temporal type. |
| 53 | + * |
| 54 | + * @param valueExtractor |
| 55 | + * @param parameter |
| 56 | + * @param temporalType |
| 57 | + * @return |
| 58 | + */ |
| 59 | + static QueryParameterSetter create(Function<JpaParametersParameterAccessor, Object> valueExtractor, |
| 60 | + Parameter<?> parameter, @Nullable TemporalType temporalType) { |
| 61 | + |
| 62 | + return temporalType == null ? new NamedOrIndexedQueryParameterSetter(valueExtractor, parameter) |
| 63 | + : new TemporalParameterSetter(valueExtractor, parameter, temporalType); |
| 64 | + } |
| 65 | + |
| 66 | + void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandler errorHandler); |
50 | 67 |
|
51 | 68 | /**
|
52 |
| - * {@link QueryParameterSetter} for named or indexed parameters that might have a {@link TemporalType} specified. |
| 69 | + * {@link QueryParameterSetter} for named or indexed parameters. |
53 | 70 | */
|
54 | 71 | class NamedOrIndexedQueryParameterSetter implements QueryParameterSetter {
|
55 | 72 |
|
56 | 73 | private final Function<JpaParametersParameterAccessor, Object> valueExtractor;
|
57 | 74 | private final Parameter<?> parameter;
|
58 |
| - private final @Nullable TemporalType temporalType; |
59 | 75 |
|
60 | 76 | /**
|
61 | 77 | * @param valueExtractor must not be {@literal null}.
|
62 | 78 | * @param parameter must not be {@literal null}.
|
63 |
| - * @param temporalType may be {@literal null}. |
64 | 79 | */
|
65 |
| - NamedOrIndexedQueryParameterSetter(Function<JpaParametersParameterAccessor, Object> valueExtractor, |
66 |
| - Parameter<?> parameter, @Nullable TemporalType temporalType) { |
| 80 | + private NamedOrIndexedQueryParameterSetter(Function<JpaParametersParameterAccessor, Object> valueExtractor, |
| 81 | + Parameter<?> parameter) { |
67 | 82 |
|
68 | 83 | Assert.notNull(valueExtractor, "ValueExtractor must not be null");
|
69 | 84 |
|
70 | 85 | this.valueExtractor = valueExtractor;
|
71 | 86 | this.parameter = parameter;
|
72 |
| - this.temporalType = temporalType; |
73 | 87 | }
|
74 | 88 |
|
75 |
| - // TODO: Refactor to use Spring's ErrorHandler instead of using a capturing ErrorHandling approach. |
76 |
| - @SuppressWarnings("unchecked") |
77 | 89 | @Override
|
78 |
| - public void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, |
79 |
| - ErrorHandling errorHandling) { |
| 90 | + public void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandler errorHandler) { |
80 | 91 |
|
81 |
| - if (temporalType != null) { |
| 92 | + Object value = valueExtractor.apply(accessor); |
82 | 93 |
|
83 |
| - Object extractedValue = valueExtractor.apply(accessor); |
84 |
| - |
85 |
| - Date value = (Date) accessor.potentiallyUnwrap(extractedValue); |
| 94 | + try { |
| 95 | + setParameter(query, value, errorHandler); |
| 96 | + } catch (RuntimeException e) { |
| 97 | + errorHandler.handleError(e); |
| 98 | + } |
| 99 | + } |
86 | 100 |
|
87 |
| - // One would think we can simply use parameter to identify the parameter we want to set. |
88 |
| - // But that does not work with list valued parameters. At least Hibernate tries to bind them by name. |
89 |
| - // TODO: move to using setParameter(Parameter, value) when https://hibernate.atlassian.net/browse/HHH-11870 is |
90 |
| - // fixed. |
| 101 | + @SuppressWarnings("unchecked") |
| 102 | + private void setParameter(BindableQuery query, Object value, ErrorHandler errorHandler) { |
91 | 103 |
|
92 |
| - if (parameter instanceof ParameterExpression) { |
93 |
| - errorHandling.execute(() -> query.setParameter((Parameter<Date>) parameter, value, temporalType)); |
94 |
| - } else if (query.hasNamedParameters() && parameter.getName() != null) { |
95 |
| - errorHandling.execute(() -> query.setParameter(parameter.getName(), value, temporalType)); |
96 |
| - } else { |
| 104 | + if (parameter instanceof ParameterExpression) { |
| 105 | + query.setParameter((Parameter<Object>) parameter, value); |
| 106 | + } else if (query.hasNamedParameters() && parameter.getName() != null) { |
| 107 | + query.setParameter(parameter.getName(), value); |
97 | 108 |
|
98 |
| - Integer position = parameter.getPosition(); |
| 109 | + } else { |
99 | 110 |
|
100 |
| - if (position != null // |
101 |
| - && (query.getParameters().size() >= parameter.getPosition() // |
102 |
| - || query.registerExcessParameters() // |
103 |
| - || errorHandling == LENIENT)) { |
| 111 | + Integer position = parameter.getPosition(); |
104 | 112 |
|
105 |
| - errorHandling.execute(() -> query.setParameter(parameter.getPosition(), value, temporalType)); |
106 |
| - } |
| 113 | + if (position != null // |
| 114 | + && (query.getParameters().size() >= position // |
| 115 | + || errorHandler == LENIENT // |
| 116 | + || query.registerExcessParameters())) { |
| 117 | + query.setParameter(position, value); |
107 | 118 | }
|
| 119 | + } |
| 120 | + } |
| 121 | + } |
108 | 122 |
|
109 |
| - } else { |
| 123 | + /** |
| 124 | + * {@link QueryParameterSetter} for named or indexed parameters that have a {@link TemporalType} specified. |
| 125 | + */ |
| 126 | + class TemporalParameterSetter implements QueryParameterSetter { |
110 | 127 |
|
111 |
| - Object value = valueExtractor.apply(accessor); |
| 128 | + private final Function<JpaParametersParameterAccessor, Object> valueExtractor; |
| 129 | + private final Parameter<?> parameter; |
| 130 | + private final TemporalType temporalType; |
| 131 | + |
| 132 | + private TemporalParameterSetter(Function<JpaParametersParameterAccessor, Object> valueExtractor, |
| 133 | + Parameter<?> parameter, TemporalType temporalType) { |
| 134 | + this.valueExtractor = valueExtractor; |
| 135 | + this.parameter = parameter; |
| 136 | + this.temporalType = temporalType; |
| 137 | + } |
| 138 | + |
| 139 | + @Override |
| 140 | + public void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandler errorHandler) { |
| 141 | + |
| 142 | + Date value = (Date) accessor.potentiallyUnwrap(valueExtractor.apply(accessor)); |
| 143 | + |
| 144 | + try { |
| 145 | + setParameter(query, value, errorHandler); |
| 146 | + } catch (RuntimeException e) { |
| 147 | + errorHandler.handleError(e); |
| 148 | + } |
| 149 | + } |
112 | 150 |
|
113 |
| - if (parameter instanceof ParameterExpression) { |
114 |
| - errorHandling.execute(() -> query.setParameter((Parameter<Object>) parameter, value)); |
115 |
| - } else if (query.hasNamedParameters() && parameter.getName() != null) { |
116 |
| - errorHandling.execute(() -> query.setParameter(parameter.getName(), value)); |
| 151 | + @SuppressWarnings("unchecked") |
| 152 | + private void setParameter(BindableQuery query, Date date, ErrorHandler errorHandler) { |
| 153 | + |
| 154 | + // One would think we can simply use parameter to identify the parameter we want to set. |
| 155 | + // But that does not work with list valued parameters. At least Hibernate tries to bind them by name. |
| 156 | + // TODO: move to using setParameter(Parameter, value) when https://hibernate.atlassian.net/browse/HHH-11870 is |
| 157 | + // fixed. |
117 | 158 |
|
118 |
| - } else { |
| 159 | + if (parameter instanceof ParameterExpression) { |
| 160 | + query.setParameter((Parameter<Date>) parameter, date, temporalType); |
| 161 | + } else if (query.hasNamedParameters() && parameter.getName() != null) { |
| 162 | + query.setParameter(parameter.getName(), date, temporalType); |
| 163 | + } else { |
119 | 164 |
|
120 |
| - Integer position = parameter.getPosition(); |
| 165 | + Integer position = parameter.getPosition(); |
121 | 166 |
|
122 |
| - if (position != null // |
123 |
| - && (query.getParameters().size() >= position // |
124 |
| - || errorHandling == LENIENT // |
125 |
| - || query.registerExcessParameters())) { |
126 |
| - errorHandling.execute(() -> query.setParameter(position, value)); |
127 |
| - } |
| 167 | + if (position != null // |
| 168 | + && (query.getParameters().size() >= parameter.getPosition() // |
| 169 | + || query.registerExcessParameters() // |
| 170 | + || errorHandler == LENIENT)) { |
| 171 | + |
| 172 | + query.setParameter(parameter.getPosition(), date, temporalType); |
128 | 173 | }
|
129 | 174 | }
|
130 | 175 | }
|
131 | 176 | }
|
132 | 177 |
|
133 |
| - enum ErrorHandling { |
| 178 | + enum ErrorHandling implements ErrorHandler { |
134 | 179 |
|
135 | 180 | STRICT {
|
136 | 181 |
|
137 | 182 | @Override
|
138 |
| - public void execute(Runnable block) { |
139 |
| - block.run(); |
| 183 | + public void handleError(Throwable t) { |
| 184 | + if (t instanceof RuntimeException rx) { |
| 185 | + throw rx; |
| 186 | + } |
| 187 | + throw new RuntimeException(t); |
140 | 188 | }
|
141 | 189 | },
|
142 | 190 |
|
143 | 191 | LENIENT {
|
144 | 192 |
|
145 | 193 | @Override
|
146 |
| - public void execute(Runnable block) { |
147 |
| - |
148 |
| - try { |
149 |
| - block.run(); |
150 |
| - } catch (RuntimeException rex) { |
151 |
| - LOG.info("Silently ignoring", rex); |
152 |
| - } |
| 194 | + public void handleError(Throwable t) { |
| 195 | + LOG.info("Silently ignoring", t); |
153 | 196 | }
|
154 | 197 | };
|
155 | 198 |
|
156 | 199 | private static final Log LOG = LogFactory.getLog(ErrorHandling.class);
|
157 |
| - |
158 |
| - abstract void execute(Runnable block); |
159 | 200 | }
|
160 | 201 |
|
161 | 202 | /**
|
|
0 commit comments