Merge branch '5.2.x'

# Conflicts:
#	build.gradle
#	spring-orm/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java
This commit is contained in:
Juergen Hoeller 2020-10-06 15:37:54 +02:00
commit b7e1553c9d
14 changed files with 142 additions and 74 deletions

View File

@ -122,8 +122,8 @@ configure(allprojects) { project ->
dependency "net.sf.ehcache:ehcache:2.10.6"
dependency "org.ehcache:jcache:1.0.1"
dependency "org.ehcache:ehcache:3.4.0"
dependency "org.hibernate:hibernate-core:5.4.21.Final"
dependency "org.hibernate:hibernate-validator:6.1.5.Final"
dependency "org.hibernate:hibernate-core:5.4.22.Final"
dependency "org.hibernate:hibernate-validator:6.1.6.Final"
dependency "org.webjars:webjars-locator-core:0.45"
dependency "org.webjars:underscorejs:1.8.3"
@ -339,7 +339,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "8.36.1"
toolVersion = "8.36.2"
configDirectory.set(rootProject.file("src/checkstyle"))
}

View File

@ -688,6 +688,7 @@ public class GenericConversionService implements ConfigurableConversionService {
}
@Override
@Nullable
public Set<ConvertiblePair> getConvertibleTypes() {
return null;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -61,12 +61,12 @@ final class MapToMapConverter implements ConditionalGenericConverter {
}
@Override
@SuppressWarnings("unchecked")
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
@SuppressWarnings("unchecked")
Map<Object, Object> sourceMap = (Map<Object, Object>) source;
// Shortcut if possible...

View File

@ -20,6 +20,7 @@ import java.util.HashSet;
import java.util.Set;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
/**
* Converts String to a Boolean.
@ -48,6 +49,7 @@ final class StringToBooleanConverter implements Converter<String, Boolean> {
@Override
@Nullable
public Boolean convert(String source) {
String value = source.trim();
if (value.isEmpty()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
/**
* Converts a String to a Character.
@ -27,6 +28,7 @@ import org.springframework.core.convert.converter.Converter;
final class StringToCharacterConverter implements Converter<String, Character> {
@Override
@Nullable
public Character convert(String source) {
if (source.isEmpty()) {
return null;

View File

@ -18,6 +18,7 @@ package org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.lang.Nullable;
/**
* Converts from a String to a {@link java.lang.Enum} by calling {@link Enum#valueOf(Class, String)}.
@ -44,6 +45,7 @@ final class StringToEnumConverterFactory implements ConverterFactory<String, Enu
}
@Override
@Nullable
public T convert(String source) {
if (source.isEmpty()) {
// It's an empty enum identifier: reset the enum value to null.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.NumberUtils;
/**
@ -55,6 +56,7 @@ final class StringToNumberConverterFactory implements ConverterFactory<String, N
}
@Override
@Nullable
public T convert(String source) {
if (source.isEmpty()) {
return null;

View File

@ -272,8 +272,7 @@ public class DefaultStompSession implements ConnectionHandlingStompSession {
}
private boolean isEmpty(@Nullable Object payload) {
return payload == null || StringUtils.isEmpty(payload) ||
(payload instanceof byte[] && ((byte[]) payload).length == 0);
return (StringUtils.isEmpty(payload) || (payload instanceof byte[] && ((byte[]) payload).length == 0));
}
private void execute(Message<byte[]> message) {

View File

@ -82,7 +82,7 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
/**
* Sub-classes can use this method to access the "native" headers sub-map.
* Subclasses can use this method to access the "native" headers sub-map.
*/
@SuppressWarnings("unchecked")
@Nullable
@ -139,6 +139,7 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
/**
* Whether the native header map contains the give header name.
* @param headerName the name of the header
*/
public boolean containsNativeHeader(String headerName) {
Map<String, List<String>> map = getNativeHeaders();
@ -146,7 +147,9 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
}
/**
* Return the values for the specified native header, if present.
* Return all values for the specified native header, if present.
* @param headerName the name of the header
* @return the associated values, or {@code null} if none
*/
@Nullable
public List<String> getNativeHeader(String headerName) {
@ -156,13 +159,15 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
/**
* Return the first value for the specified native header, if present.
* @param headerName the name of the header
* @return the associated value, or {@code null} if none
*/
@Nullable
public String getFirstNativeHeader(String headerName) {
Map<String, List<String>> map = getNativeHeaders();
if (map != null) {
List<String> values = map.get(headerName);
if (values != null) {
if (!CollectionUtils.isEmpty(values)) {
return values.get(0);
}
}
@ -200,6 +205,8 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
* Add the specified native header value to existing values.
* <p>In order for this to work, the accessor must be {@link #isMutable()
* mutable}. See {@link MessageHeaderAccessor} for details.
* @param name the name of the header
* @param value the header value to set
*/
public void addNativeHeader(String name, @Nullable String value) {
Assert.state(isMutable(), "Already immutable");
@ -216,6 +223,10 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
setModified(true);
}
/**
* Add the specified native headers to existing values.
* @param headers the headers to set
*/
public void addNativeHeaders(@Nullable MultiValueMap<String, String> headers) {
if (headers == null) {
return;
@ -227,24 +238,34 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
* Remove the specified native header value replacing existing values.
* <p>In order for this to work, the accessor must be {@link #isMutable()
* mutable}. See {@link MessageHeaderAccessor} for details.
* @param headerName the name of the header
* @return the associated values, or {@code null} if the header was not present
*/
@Nullable
public List<String> removeNativeHeader(String name) {
public List<String> removeNativeHeader(String headerName) {
Assert.state(isMutable(), "Already immutable");
Map<String, List<String>> nativeHeaders = getNativeHeaders();
if (nativeHeaders == null) {
if (CollectionUtils.isEmpty(nativeHeaders)) {
return null;
}
return nativeHeaders.remove(name);
return nativeHeaders.remove(headerName);
}
/**
* Return the first value for the specified native header,
* or {@code null} if none.
* @param headerName the name of the header
* @param headers the headers map to introspect
* @return the associated value, or {@code null} if none
*/
@SuppressWarnings("unchecked")
@Nullable
public static String getFirstNativeHeader(String headerName, Map<String, Object> headers) {
Map<String, List<String>> map = (Map<String, List<String>>) headers.get(NATIVE_HEADERS);
if (map != null) {
List<String> values = map.get(headerName);
if (values != null) {
if (!CollectionUtils.isEmpty(values)) {
return values.get(0);
}
}

View File

@ -211,6 +211,7 @@ public class LocalSessionFactoryBuilder extends Configuration {
"Unknown transaction manager type: " + jtaTransactionManager.getClass().getName());
}
getProperties().put(AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, "jta");
getProperties().put(AvailableSettings.CONNECTION_HANDLING,
PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT);

View File

@ -700,8 +700,8 @@ public class MockHttpServletRequestBuilder
String query = this.url.getRawQuery();
if (!this.queryParams.isEmpty()) {
String s = UriComponentsBuilder.newInstance().queryParams(this.queryParams).build().encode().getQuery();
query = StringUtils.isEmpty(query) ? s : query + "&" + s;
String str = UriComponentsBuilder.newInstance().queryParams(this.queryParams).build().encode().getQuery();
query = StringUtils.hasLength(query) ? (query + "&" + str) : str;
}
if (query != null) {
request.setQueryString(query);

View File

@ -245,10 +245,10 @@ public final class ResponseCookie extends HttpCookie {
@Nullable
private String initDomain(String domain) {
if (lenient && !StringUtils.isEmpty(domain)) {
String s = domain.trim();
if (s.startsWith("\"") && s.endsWith("\"")) {
if (s.substring(1, s.length() - 1).trim().isEmpty()) {
if (lenient && StringUtils.hasLength(domain)) {
String str = domain.trim();
if (str.startsWith("\"") && str.endsWith("\"")) {
if (str.substring(1, str.length() - 1).trim().isEmpty()) {
return null;
}
}

View File

@ -38,7 +38,6 @@ import org.springframework.core.io.buffer.PooledDataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
/**
* {@link ClientHttpRequest} implementation for the Jetty ReactiveStreams HTTP client.
@ -62,9 +61,7 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
@Override
public HttpMethod getMethod() {
HttpMethod method = HttpMethod.resolve(this.jettyRequest.getMethod());
Assert.state(method != null, "Method must not be null");
return method;
return HttpMethod.valueOf(this.jettyRequest.getMethod());
}
@Override
@ -122,7 +119,6 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
public void succeeded() {
DataBufferUtils.release(buffer);
}
@Override
public void failed(Throwable x) {
DataBufferUtils.release(buffer);

View File

@ -381,6 +381,19 @@ NOTE: The preceding definition of the `dataSource` bean uses the `<jndi-lookup/>
from the `jee` namespace. For more information see
<<integration.adoc#xsd-schemas-jee, The JEE Schema>>.
NOTE: If you use JTA, your transaction manager definition should look the same, regardless
of what data access technology you use, be it JDBC, Hibernate JPA, or any other supported
technology. This is due to the fact that JTA transactions are global transactions, which
can enlist any transactional resource.
In all Spring transaction setups, application code does not need to change. You can change
how transactions are managed merely by changing configuration, even if that change means
moving from local to global transactions or vice versa.
[[transaction-strategies-hibernate]]
==== Hibernate Transaction Setup
You can also easily use Hibernate local transactions, as shown in the following examples.
In this case, you need to define a Hibernate `LocalSessionFactoryBean`, which your
application code can use to obtain Hibernate `Session` instances.
@ -420,21 +433,52 @@ example declares `sessionFactory` and `txManager` beans:
If you use Hibernate and Java EE container-managed JTA transactions, you should use the
same `JtaTransactionManager` as in the previous JTA example for JDBC, as the following
example shows:
example shows. Also, it is recommended to make Hibernate aware of JTA through its
transaction coordinator and possibly also its connection release mode configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.transaction.coordinator_class=jta
hibernate.connection.handling_mode=DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
----
NOTE: If you use JTA, your transaction manager definition should look the same, regardless
of what data access technology you use, be it JDBC, Hibernate JPA, or any other supported
technology. This is due to the fact that JTA transactions are global transactions, which
can enlist any transactional resource.
Or alternatively, you may pass the `JtaTransactionManager` into your `LocalSessionFactoryBean`
for enforcing the same defaults:
In all these cases, application code does not need to change. You can change how
transactions are managed merely by changing configuration, even if that change means
moving from local to global transactions or vice versa.
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
<property name="jtaTransactionManager" ref="txManager"/>
</bean>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
----
@ -3118,8 +3162,8 @@ hierarchy defined in the `org.springframework.dao` package. (See <<dao-exception
When you use the `JdbcTemplate` for your code, you need only to implement callback
interfaces, giving them a clearly defined contract. Given a `Connection` provided by the
`JdbcTemplate` class, the `PreparedStatementCreator`
callback interface creates a prepared statement, providing SQL and any necessary parameters. The same is true for the
`JdbcTemplate` class, the `PreparedStatementCreator` callback interface creates a prepared
statement, providing SQL and any necessary parameters. The same is true for the
`CallableStatementCreator` interface, which creates callable statements. The
`RowCallbackHandler` interface extracts values from each row of a `ResultSet`.
@ -7800,12 +7844,12 @@ conjunction with EJBs.
==== Spurious Application Server Warnings with Hibernate
In some JTA environments with very strict `XADataSource` implementations (currently
only some WebLogic Server and WebSphere versions), when Hibernate is configured without
regard to the JTA `PlatformTransactionManager` object for that environment,
spurious warning or exceptions can show up in the application server log.
These warnings or exceptions indicate that the connection being accessed is no longer
valid or JDBC access is no longer valid, possibly because the transaction is no longer
active. As an example, here is an actual exception from WebLogic:
some WebLogic Server and WebSphere versions), when Hibernate is configured without
regard to the JTA transaction manager for that environment, spurious warnings or
exceptions can show up in the application server log. These warnings or exceptions
indicate that the connection being accessed is no longer valid or JDBC access is no
longer valid, possibly because the transaction is no longer active. As an example,
here is an actual exception from WebLogic:
[literal]
[subs="verbatim,quotes"]
@ -7814,28 +7858,26 @@ java.sql.SQLException: The transaction is no longer active - status: 'Committed'
further JDBC access is allowed within this transaction.
----
You can resolve this warning by making Hibernate aware of the JTA
`PlatformTransactionManager` instance, to which it synchronizes (along with Spring).
You have two options for doing this:
Another common problem is a connection leak after JTA transactions, with Hibernate
sessions (and potentially underlying JDBC connections) not getting closed properly.
* If, in your application context, you already directly obtain the JTA
`PlatformTransactionManager` object (presumably from JNDI through
`JndiObjectFactoryBean` or `<jee:jndi-lookup>`) and feed it, for example, to
Spring's `JtaTransactionManager`, the easiest way is to specify a reference to
the bean that defines this JTA `PlatformTransactionManager` instance as the value of the
`jtaTransactionManager` property for `LocalSessionFactoryBean.` Spring then makes the
object available to Hibernate.
* More likely, you do not already have the JTA `PlatformTransactionManager` instance,
because Spring's `JtaTransactionManager` can find it itself. Thus, you need to
configure Hibernate to look up JTA `PlatformTransactionManager` directly. You do this
by configuring an application server-specific `TransactionManagerLookup` class in the
Hibernate configuration, as described in the Hibernate manual.
You can resolve such issues by making Hibernate aware of the JTA transaction manager,
to which it synchronizes (along with Spring). You have two options for doing this:
* Pass your Spring `JtaTransactionManager` bean to your Hibernate setup. The easiest
way is a bean reference into the `jtaTransactionManager` property for your
`LocalSessionFactoryBean` bean (see <<transaction-strategies-hibernate>>).
Spring then makes the corresponding JTA strategies available to Hibernate.
* You may also configure Hibernate's JTA-related properties explicitly, in particular
"hibernate.transaction.coordinator_class", "hibernate.connection.handling_mode"
and potentially "hibernate.transaction.jta.platform" in your "hibernateProperties"
on `LocalSessionFactoryBean` (see Hibernate's manual for details on those properties).
The remainder of this section describes the sequence of events that occur with and
without Hibernate's awareness of the JTA `PlatformTransactionManager`.
When Hibernate is not configured with any awareness of the JTA
`PlatformTransactionManager`, the following events occur when a JTA transaction commits:
When Hibernate is not configured with any awareness of the JTA transaction manager,
the following events occur when a JTA transaction commits:
* The JTA transaction commits.
* Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so it is
@ -7848,16 +7890,16 @@ When Hibernate is not configured with any awareness of the JTA
error, as the application server no longer considers the `Connection` to be usable,
because the transaction has already been committed.
When Hibernate is configured with awareness of the JTA `PlatformTransactionManager`, the
following events occur when a JTA transaction commits:
When Hibernate is configured with awareness of the JTA transaction manager,
the following events occur when a JTA transaction commits:
* The JTA transaction is ready to commit.
* Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so the
transaction is called back through a `beforeCompletion` callback by the JTA
transaction manager.
* Spring is aware that Hibernate itself is synchronized to the JTA transaction and
behaves differently than in the previous scenario. Assuming the Hibernate `Session`
needs to be closed at all, Spring closes it now.
behaves differently than in the previous scenario. In particular, it aligns with
Hibernate's transactional resource management.
* The JTA transaction commits.
* Hibernate is synchronized to the JTA transaction, so the transaction is called back
through an `afterCompletion` callback by the JTA transaction manager and can
@ -7888,13 +7930,13 @@ that is used by the application to obtain an entity manager.
[[orm-jpa-setup-lemfb]]
===== Using `LocalEntityManagerFactoryBean`
You can use this option only in simple deployment environments such as stand-alone applications
and integration tests.
You can use this option only in simple deployment environments such as stand-alone
applications and integration tests.
The `LocalEntityManagerFactoryBean` creates an `EntityManagerFactory` suitable for
simple deployment environments where the application uses only JPA for data access. The
factory bean uses the JPA `PersistenceProvider` auto-detection mechanism (according to
JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the
simple deployment environments where the application uses only JPA for data access.
The factory bean uses the JPA `PersistenceProvider` auto-detection mechanism (according
to JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the
persistence unit name. The following XML example configures such a bean:
[source,xml,indent=0,subs="verbatim,quotes"]
@ -8378,7 +8420,7 @@ steps:
your transaction coordinator. This is usually straightforward in a Java EE environment,
exposing a different kind of `DataSource` through JNDI. See your application server
documentation for details. Analogously, a standalone transaction coordinator usually
comes with special XA-integrated `DataSource` implementations. Again, check its documentation.
comes with special XA-integrated `DataSource` variants. Again, check its documentation.
* The JPA `EntityManagerFactory` setup needs to be configured for JTA. This is
provider-specific, typically through special properties to be specified as `jpaProperties`
@ -8387,11 +8429,11 @@ are even version-specific. See your Hibernate documentation for details.
* Spring's `HibernateJpaVendorAdapter` enforces certain Spring-oriented defaults, such
as the connection release mode, `on-close`, which matches Hibernate's own default in
Hibernate 5.0 but not any more in 5.1/5.2. For a JTA setup, either do not declare
`HibernateJpaVendorAdapter` to begin with or turn off its `prepareConnection` flag.
Alternatively, set Hibernate 5.2's `hibernate.connection.handling_mode` property to
Hibernate 5.0 but not any more in Hibernate 5.1+. For a JTA setup, make sure to declare
your persistence unit transaction type as "JTA". Alternatively, set Hibernate 5.2's
`hibernate.connection.handling_mode` property to
`DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT` to restore Hibernate's own default.
See <<orm-hibernate-invalid-jdbc-access-error>> for a related note about WebLogic.
See <<orm-hibernate-invalid-jdbc-access-error>> for related notes.
* Alternatively, consider obtaining the `EntityManagerFactory` from your application
server itself (that is, through a JNDI lookup instead of a locally declared