Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public class DsTransactionalTest {
private AccountService accountService;
@Autowired
private ProductService productService;
@Autowired
private NonDatabaseConnectionService nonDatabaseConnectionService;
private DynamicRoutingDataSource ds;

@Test
Expand Down Expand Up @@ -84,6 +86,19 @@ public void testDsTransactional() {
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 15));
}

@Test
public void testRequiredWithRequiresNewNoConnection() {
// Setup datasources
ds = (DynamicRoutingDataSource) dataSource;
if (!ds.getDataSources().containsKey("order")) {
DataSourceProperty orderDataSourceProperty = createDataSourceProperty("order");
ds.addDataSource(orderDataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(orderDataSourceProperty));
}

// This should not throw NPE even though the inner REQUIRES_NEW transaction has no JDBC connections
nonDatabaseConnectionService.outerRequiredWithConnection();
}

private DataSourceProperty createDataSourceProperty(String poolName) {
DataSourceProperty result = new DataSourceProperty();
result.setPoolName(poolName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ public static void notify(String xid, Boolean state) throws Exception {
boolean hasSavepoint = hasSavepoint(xid);
List<SavePointHolder> savePointHolders = savePointMap.get(xid);
Map<String, ConnectionProxy> connectionProxyMap = concurrentHashMap.get(xid);
if (connectionProxyMap == null) {
return;
}
try {
//If there is a savepoint,Indicates a nested transaction.
if (hasSavepoint) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright © 2018 organization baomidou
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baomidou.dynamic.datasource.common.service.tx;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.tx.DsPropagation;
import org.springframework.stereotype.Service;

/**
* Service with REQUIRES_NEW transaction but no JDBC connections
*/
@Service
@DS("order")
public class NoConnectionService {

/**
* Inner REQUIRES_NEW transaction without JDBC connection
* This should not throw NPE when committing
*/
@DSTransactional(propagation = DsPropagation.REQUIRES_NEW)
public void innerRequiresNewWithoutConnection() {
// No database operations - just business logic
System.out.println("Business logic without database operations");

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid writing to stdout from this Spring @service. It will add noisy console output to the test suite (and potentially to any app that happens to include test-common). Prefer removing the print entirely or using the project’s logger (e.g., Slf4j) if you need an observable side-effect.

Suggested change
System.out.println("Business logic without database operations");

Copilot uses AI. Check for mistakes.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright © 2018 organization baomidou
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baomidou.dynamic.datasource.common.service.tx;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.tx.DsPropagation;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

/**
* Test service to reproduce the NPE issue when REQUIRES_NEW has no JDBC connections
*/
@Service
@DS("order")
public class NonDatabaseConnectionService {
private final DataSource dataSource;
private final NoConnectionService noConnectionService;

public NonDatabaseConnectionService(DataSource dataSource, NoConnectionService noConnectionService) {
this.dataSource = dataSource;
this.noConnectionService = noConnectionService;
}

/**
* Outer REQUIRED transaction with JDBC connection
*/
@DSTransactional(propagation = DsPropagation.REQUIRED)
public void outerRequiredWithConnection() {
// Trigger JDBC connection
triggerJdbcConnection();
// Call nested REQUIRES_NEW without JDBC connection
noConnectionService.innerRequiresNewWithoutConnection();
}

/**
* Trigger a JDBC connection
*/
private void triggerJdbcConnection() {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT COUNT(*) FROM p_order")) {
if (resultSet.next()) {
resultSet.getInt(1);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Loading