|
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