Skip to content

Commit fd17df9

Browse files
committed
Merge branch '6.0.x'
# Conflicts: # spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java
2 parents 5199274 + 384246c commit fd17df9

File tree

7 files changed

+81
-56
lines changed

7 files changed

+81
-56
lines changed

framework-docs/modules/ROOT/pages/integration/scheduling.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,12 @@ Notice that the methods to be scheduled must have void returns and must not acce
380380
arguments. If the method needs to interact with other objects from the application
381381
context, those would typically have been provided through dependency injection.
382382

383+
`@Scheduled` can be used as a repeatable annotation. If several scheduled declarations
384+
are found on the same method, each of them will be processed independently, with a
385+
separate trigger firing for each of them. As a consequence, such co-located schedules
386+
may overlap and execute multiple times in parallel or in immediate succession.
387+
Please make sure that your specified cron expressions etc do not accidentally overlap.
388+
383389
[NOTE]
384390
====
385391
As of Spring Framework 4.3, `@Scheduled` methods are supported on beans of any scope.

spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929

3030
/**
3131
* Annotation that marks a method to be scheduled. Exactly one of the
32-
* {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes must be
33-
* specified.
32+
* {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes
33+
* must be specified.
3434
*
3535
* <p>The annotated method must not accept arguments. It will typically have
3636
* a {@code void} return type; if not, the returned value will be ignored
@@ -56,7 +56,10 @@
5656
* or {@link EnableScheduling @EnableScheduling} annotation.
5757
*
5858
* <p>This annotation can be used as a <em>{@linkplain Repeatable repeatable}</em>
59-
* annotation.
59+
* annotation. If several scheduled declarations are found on the same method,
60+
* each of them will be processed independently, with a separate trigger firing
61+
* for each of them. As a consequence, such co-located schedules may overlap
62+
* and execute multiple times in parallel or in immediate succession.
6063
*
6164
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
6265
* <em>composed annotations</em> with attribute overrides.

spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ void bonaFideMBeanIsNotExportedWhenAutodetectIsTotallyTurnedOff() {
354354
exporter.setBeans(beansToExport);
355355
exporter.setServer(getServer());
356356
exporter.setBeanFactory(factory);
357-
exporter.setAutodetectMode(MBeanExporter.AUTODETECT_NONE);
357+
exporter.setAutodetect(false);
358358
// MBean has a bad ObjectName, so if said MBean is autodetected, an exception will be thrown...
359359
start(exporter);
360360
}
@@ -524,18 +524,16 @@ void setAutodetectModeNameToSupportedValue() {
524524
}
525525

526526
@Test
527-
void notRunningInBeanFactoryAndPassedBeanNameToExport() throws Exception {
527+
void notRunningInBeanFactoryAndPassedBeanNameToExport() {
528528
Map<String, Object> beans = Map.of(OBJECT_NAME, "beanName");
529529
exporter.setBeans(beans);
530-
assertThatExceptionOfType(MBeanExportException.class)
531-
.isThrownBy(() -> start(exporter));
530+
assertThatExceptionOfType(MBeanExportException.class).isThrownBy(() -> start(exporter));
532531
}
533532

534533
@Test
535-
void notRunningInBeanFactoryAndAutodetectionIsOn() throws Exception {
536-
exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL);
537-
assertThatExceptionOfType(MBeanExportException.class)
538-
.isThrownBy(() -> start(exporter));
534+
void notRunningInBeanFactoryAndAutodetectionIsOn() {
535+
exporter.setAutodetect(true);
536+
assertThatExceptionOfType(MBeanExportException.class).isThrownBy(() -> start(exporter));
539537
}
540538

541539
@Test // SPR-2158
@@ -556,7 +554,7 @@ void mbeanIsNotUnregisteredSpuriouslyIfSomeExternalProcessHasUnregisteredMBean()
556554
}
557555

558556
@Test // SPR-3302
559-
void beanNameCanBeUsedInNotificationListenersMap() throws Exception {
557+
void beanNameCanBeUsedInNotificationListenersMap() {
560558
String beanName = "charlesDexterWard";
561559
BeanDefinitionBuilder testBean = BeanDefinitionBuilder.rootBeanDefinition(JmxTestBean.class);
562560

@@ -576,7 +574,7 @@ void beanNameCanBeUsedInNotificationListenersMap() throws Exception {
576574
}
577575

578576
@Test
579-
void wildcardCanBeUsedInNotificationListenersMap() throws Exception {
577+
void wildcardCanBeUsedInNotificationListenersMap() {
580578
String beanName = "charlesDexterWard";
581579
BeanDefinitionBuilder testBean = BeanDefinitionBuilder.rootBeanDefinition(JmxTestBean.class);
582580

@@ -636,24 +634,23 @@ void ignoreBeanName() throws MalformedObjectNameException {
636634
exporter.setServer(getServer());
637635
exporter.setAssembler(new NamedBeanAutodetectCapableMBeanInfoAssemblerStub(firstBeanName, secondBeanName));
638636
exporter.setBeanFactory(factory);
639-
exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL);
637+
exporter.setAutodetect(true);
640638
exporter.addExcludedBean(secondBeanName);
641639

642640
start(exporter);
643-
assertIsRegistered("Bean not autodetected in (AUTODETECT_ALL) mode",
644-
ObjectNameManager.getInstance(firstBeanName));
645-
assertIsNotRegistered("Bean should have been excluded",
646-
ObjectNameManager.getInstance(secondBeanName));
641+
assertIsRegistered("Bean not autodetected", ObjectNameManager.getInstance(firstBeanName));
642+
assertIsNotRegistered("Bean should have been excluded", ObjectNameManager.getInstance(secondBeanName));
647643
}
648644

649645
@Test
650646
void registerFactoryBean() throws MalformedObjectNameException {
651647
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
652-
factory.registerBeanDefinition("spring:type=FactoryBean", new RootBeanDefinition(ProperSomethingFactoryBean.class));
648+
factory.registerBeanDefinition("spring:type=FactoryBean",
649+
new RootBeanDefinition(ProperSomethingFactoryBean.class));
653650

654651
exporter.setServer(getServer());
655652
exporter.setBeanFactory(factory);
656-
exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL);
653+
exporter.setAutodetect(true);
657654

658655
start(exporter);
659656
assertIsRegistered("Non-null FactoryBean object registered",
@@ -663,11 +660,12 @@ void registerFactoryBean() throws MalformedObjectNameException {
663660
@Test
664661
void ignoreNullObjectFromFactoryBean() throws MalformedObjectNameException {
665662
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
666-
factory.registerBeanDefinition("spring:type=FactoryBean", new RootBeanDefinition(NullSomethingFactoryBean.class));
663+
factory.registerBeanDefinition("spring:type=FactoryBean",
664+
new RootBeanDefinition(NullSomethingFactoryBean.class));
667665

668666
exporter.setServer(getServer());
669667
exporter.setBeanFactory(factory);
670-
exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL);
668+
exporter.setAutodetect(true);
671669

672670
start(exporter);
673671
assertIsNotRegistered("Null FactoryBean object not registered",

spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcAccessor.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -80,10 +80,11 @@ protected DataSource obtainDataSource() {
8080
}
8181

8282
/**
83-
* Specify the database product name for the DataSource that this accessor uses.
84-
* This allows to initialize an SQLErrorCodeSQLExceptionTranslator without
85-
* obtaining a Connection from the DataSource to get the meta-data.
83+
* Specify the database product name for the {@code DataSource} that this accessor uses.
84+
* This allows for initializing a {@link SQLErrorCodeSQLExceptionTranslator} without
85+
* obtaining a {@code Connection} from the {@code DataSource} to get the meta-data.
8686
* @param dbName the database product name that identifies the error codes entry
87+
* @see #setExceptionTranslator
8788
* @see SQLErrorCodeSQLExceptionTranslator#setDatabaseProductName
8889
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
8990
*/
@@ -98,22 +99,20 @@ public void setDatabaseProductName(String dbName) {
9899

99100
/**
100101
* Set the exception translator for this instance.
101-
* <p>If no custom translator is provided, a default
102-
* {@link SQLErrorCodeSQLExceptionTranslator} is used
103-
* which examines the SQLException's vendor-specific error code.
102+
* <p>A {@link SQLErrorCodeSQLExceptionTranslator} used by default if a user-provided
103+
* `sql-error-codes.xml` file has been found in the root of the classpath. Otherwise,
104+
* {@link SQLExceptionSubclassTranslator} serves as the default translator as of 6.0.
104105
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
105-
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
106+
* @see org.springframework.jdbc.support.SQLExceptionSubclassTranslator
106107
*/
107108
public void setExceptionTranslator(SQLExceptionTranslator exceptionTranslator) {
108109
this.exceptionTranslator = exceptionTranslator;
109110
}
110111

111112
/**
112-
* Return the exception translator for this instance.
113-
* <p>Creates a default {@link SQLErrorCodeSQLExceptionTranslator}
114-
* for the specified DataSource if none set, or a
115-
* {@link SQLStateSQLExceptionTranslator} in case of no DataSource.
116-
* @see #getDataSource()
113+
* Return the exception translator to use for this instance,
114+
* creating a default if necessary.
115+
* @see #setExceptionTranslator
117116
*/
118117
public SQLExceptionTranslator getExceptionTranslator() {
119118
SQLExceptionTranslator exceptionTranslator = this.exceptionTranslator;

spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcTransactionManager.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -59,16 +59,16 @@ public class JdbcTransactionManager extends DataSourceTransactionManager {
5959

6060

6161
/**
62-
* Create a new JdbcTransactionManager instance.
63-
* A DataSource has to be set to be able to use it.
62+
* Create a new {@code JdbcTransactionManager} instance.
63+
* A {@code DataSource} has to be set to be able to use it.
6464
* @see #setDataSource
6565
*/
6666
public JdbcTransactionManager() {
6767
super();
6868
}
6969

7070
/**
71-
* Create a new JdbcTransactionManager instance.
71+
* Create a new {@code JdbcTransactionManager} instance.
7272
* @param dataSource the JDBC DataSource to manage transactions for
7373
*/
7474
public JdbcTransactionManager(DataSource dataSource) {
@@ -79,13 +79,15 @@ public JdbcTransactionManager(DataSource dataSource) {
7979

8080

8181
/**
82-
* Specify the database product name for the DataSource that this transaction manager
83-
* uses. This allows to initialize an SQLErrorCodeSQLExceptionTranslator without
84-
* obtaining a Connection from the DataSource to get the meta-data.
82+
* Specify the database product name for the {@code DataSource} that this
83+
* transaction manager operates on.
84+
* This allows for initializing a {@link SQLErrorCodeSQLExceptionTranslator} without
85+
* obtaining a {@code Connection} from the {@code DataSource} to get the meta-data.
8586
* @param dbName the database product name that identifies the error codes entry
86-
* @see JdbcAccessor#setDatabaseProductName
87+
* @see #setExceptionTranslator
8788
* @see SQLErrorCodeSQLExceptionTranslator#setDatabaseProductName
8889
* @see java.sql.DatabaseMetaData#getDatabaseProductName()
90+
* @see JdbcAccessor#setDatabaseProductName
8991
*/
9092
public void setDatabaseProductName(String dbName) {
9193
if (SQLErrorCodeSQLExceptionTranslator.hasUserProvidedErrorCodesFile()) {
@@ -97,22 +99,22 @@ public void setDatabaseProductName(String dbName) {
9799
}
98100

99101
/**
100-
* Set the exception translator for this instance.
101-
* <p>If no custom translator is provided, a default
102-
* {@link SQLErrorCodeSQLExceptionTranslator} is used
103-
* which examines the SQLException's vendor-specific error code.
104-
* @see JdbcAccessor#setExceptionTranslator
102+
* Set the exception translator for this transaction manager.
103+
* <p>A {@link SQLErrorCodeSQLExceptionTranslator} used by default if a user-provided
104+
* `sql-error-codes.xml` file has been found in the root of the classpath. Otherwise,
105+
* {@link SQLExceptionSubclassTranslator} serves as the default translator as of 6.0.
105106
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
107+
* @see org.springframework.jdbc.support.SQLExceptionSubclassTranslator
108+
* @see JdbcAccessor#setExceptionTranslator
106109
*/
107110
public void setExceptionTranslator(SQLExceptionTranslator exceptionTranslator) {
108111
this.exceptionTranslator = exceptionTranslator;
109112
}
110113

111114
/**
112-
* Return the exception translator for this instance.
113-
* <p>Creates a default {@link SQLErrorCodeSQLExceptionTranslator}
114-
* for the specified DataSource if none set.
115-
* @see #getDataSource()
115+
* Return the exception translator to use for this instance,
116+
* creating a default if necessary.
117+
* @see #setExceptionTranslator
116118
*/
117119
public SQLExceptionTranslator getExceptionTranslator() {
118120
SQLExceptionTranslator exceptionTranslator = this.exceptionTranslator;

spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -51,6 +51,8 @@
5151
* <p>Falls back to a standard {@link SQLStateSQLExceptionTranslator} if the JDBC
5252
* driver does not actually expose JDBC 4 compliant {@code SQLException} subclasses.
5353
*
54+
* <p>This translator serves as the default translator as of 6.0.
55+
*
5456
* @author Thomas Risberg
5557
* @author Juergen Hoeller
5658
* @since 2.5
@@ -72,7 +74,7 @@ protected DataAccessException doTranslate(String task, @Nullable String sql, SQL
7274
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
7375
}
7476
if (ex instanceof SQLTransactionRollbackException) {
75-
if ("40001".equals(ex.getSQLState())) {
77+
if (SQLStateSQLExceptionTranslator.indicatesCannotAcquireLock(ex.getSQLState())) {
7678
return new CannotAcquireLockException(buildMessage(task, sql, ex), ex);
7779
}
7880
return new PessimisticLockingFailureException(buildMessage(task, sql, ex), ex);

spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,16 @@
3939
* does not require special initialization (no database vendor detection, etc.).
4040
* For more precise translation, consider {@link SQLErrorCodeSQLExceptionTranslator}.
4141
*
42+
* <p>This translator is commonly used as a {@link #setFallbackTranslator fallback}
43+
* behind a primary translator such as {@link SQLErrorCodeSQLExceptionTranslator} or
44+
* {@link SQLExceptionSubclassTranslator}.
45+
*
4246
* @author Rod Johnson
4347
* @author Juergen Hoeller
4448
* @author Thomas Risberg
4549
* @see java.sql.SQLException#getSQLState()
4650
* @see SQLErrorCodeSQLExceptionTranslator
51+
* @see SQLExceptionSubclassTranslator
4752
*/
4853
public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLExceptionTranslator {
4954

@@ -111,7 +116,7 @@ else if (TRANSIENT_DATA_ACCESS_RESOURCE_CODES.contains(classCode)) {
111116
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
112117
}
113118
else if (PESSIMISTIC_LOCKING_FAILURE_CODES.contains(classCode)) {
114-
if ("40001".equals(sqlState)) {
119+
if (indicatesCannotAcquireLock(sqlState)) {
115120
return new CannotAcquireLockException(buildMessage(task, sql, ex), ex);
116121
}
117122
return new PessimisticLockingFailureException(buildMessage(task, sql, ex), ex);
@@ -148,9 +153,10 @@ private String getSqlState(SQLException ex) {
148153
return sqlState;
149154
}
150155

156+
151157
/**
152158
* Check whether the given SQL state (and the associated error code in case
153-
* of a generic SQL state value) indicate a duplicate key exception:
159+
* of a generic SQL state value) indicate a {@link DuplicateKeyException}:
154160
* either SQL state 23505 as a specific indication, or the generic SQL state
155161
* 23000 with well-known vendor codes (1 for Oracle, 1062 for MySQL/MariaDB,
156162
* 2601/2627 for MS SQL Server).
@@ -163,4 +169,13 @@ static boolean indicatesDuplicateKey(@Nullable String sqlState, int errorCode) {
163169
(errorCode == 1 || errorCode == 1062 || errorCode == 2601 || errorCode == 2627)));
164170
}
165171

172+
/**
173+
* Check whether the given SQL state indicates a {@link CannotAcquireLockException},
174+
* with SQL state 40001 as a specific indication.
175+
* @param sqlState the SQL state value
176+
*/
177+
static boolean indicatesCannotAcquireLock(@Nullable String sqlState) {
178+
return "40001".equals(sqlState);
179+
}
180+
166181
}

0 commit comments

Comments
 (0)