Skip to content

Commit 2fbb3b0

Browse files
schaudergregturn
authored andcommitted
DATAJDBC-136 - Fix ClassNotFoundException when MyBatis is not on the classpath.
1 parent 1291b5d commit 2fbb3b0

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,22 @@ private CascadingDataAccessStrategy createDataAccessStrategy(JdbcMappingContext
118118

119119
private Optional<DataAccessStrategy> createMyBatisDataAccessStrategy() {
120120

121-
if (!ClassUtils.isPresent("org.apache.ibatis.session.SqlSessionFactory", this.getClass().getClassLoader())) {
121+
String myBatisSqlSessionFactoryClassName = "org.apache.ibatis.session.SqlSessionFactory";
122+
ClassLoader classLoader = this.getClass().getClassLoader();
123+
124+
if (!ClassUtils.isPresent(myBatisSqlSessionFactoryClassName, classLoader)) {
122125
return Optional.empty();
123126
}
124127

125-
return getBean(SqlSessionFactory.class, SQL_SESSION_FACTORY_BEAN_NAME)
126-
.map(ssf -> new MyBatisDataAccessStrategy(ssf));
128+
try {
129+
130+
return getBean(classLoader.loadClass(myBatisSqlSessionFactoryClassName), SQL_SESSION_FACTORY_BEAN_NAME)
131+
// note that the cast to SqlSessionFactory happens in a lambda, which is basically a separate class
132+
// thus it won't get loaded if this code path doesn't get executed.
133+
.map(ssf -> new MyBatisDataAccessStrategy((SqlSessionFactory) ssf));
134+
} catch (ClassNotFoundException e) {
135+
throw new IllegalStateException("Detected MyBatis on classpath but failed to load the class " + myBatisSqlSessionFactoryClassName);
136+
}
127137
}
128138

129139
private Optional<DataAccessStrategy> createDefaultAccessStrategy(JdbcMappingContext context,

src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy;
2626
import org.springframework.data.repository.CrudRepository;
2727
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
28+
import org.springframework.instrument.classloading.ShadowingClassLoader;
2829
import org.springframework.jdbc.core.JdbcOperations;
2930
import org.springframework.jdbc.core.JdbcTemplate;
3031
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
32+
import org.springframework.util.ReflectionUtils;
3133

3234
/**
3335
* Tests the dependency injection for {@link JdbcRepositoryFactoryBean}.
@@ -211,6 +213,29 @@ public void withSqlSessionFactoryThereIsMyBatisIntegration() {
211213
assertThat(findDataAccessStrategy(factory, MyBatisDataAccessStrategy.class)).isNotNull();
212214
}
213215

216+
@Test // DATAJDBC-136
217+
public void canBeLoadedWithoutMyBatis() throws Exception {
218+
219+
String sqlSessionFactoryClassName = SqlSessionFactory.class.getName();
220+
221+
ShadowingClassLoader classLoader = new ShadowingClassLoader(this.getClass().getClassLoader()) {
222+
223+
@Override
224+
public Class<?> loadClass(String name) throws ClassNotFoundException {
225+
226+
if (name.equals(sqlSessionFactoryClassName)) {
227+
throw new ClassNotFoundException("%s is configured not to get loaded by this classloader");
228+
}
229+
return super.loadClass(name);
230+
}
231+
};
232+
233+
Class<?> loadedClass = classLoader.loadClass(JdbcRepositoryFactoryBean.class.getName());
234+
235+
assertThat(loadedClass).isNotNull();
236+
ReflectionUtils.getAllDeclaredMethods(loadedClass);
237+
}
238+
214239
private Condition<? super RepositoryFactorySupport> using(NamedParameterJdbcOperations expectedOperations) {
215240

216241
Predicate<RepositoryFactorySupport> predicate = r -> extractNamedParameterJdbcOperations(r) == expectedOperations;

0 commit comments

Comments
 (0)