Spring boot, кажется, не может найти конфигурации mybatis mapper (коллекция Mapped Statements не содержит значения)
Я работаю с mybatis и spring-boot.
Я страдал в течение нескольких часов с последующим сообщением.
2018-02-18 15:25:13.774 ERROR 77556 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for org.owls.mybatis.mapper.TestMapper.selectTimestamp
### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for org.owls.mybatis.mapper.TestMapper.selectTimestamp] with root cause
java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for org.owls.mybatis.mapper.TestMapper.selectTimestamp
Мой источник основан на этом(Spring boot, начать с проекта) и добавить настройки Mybatis.
Я думаю, что каким-то образом mybatis не смог найти информацию о сопоставителе, поэтому я хотел бы напечатать список зарегистрированных сопоставителей mybatis в консоли. (Является ли это возможным?)
К вашему сведению, следуйте тому, что я реализовал.
Я использую application.yml
добавить mybatis
mybatis:
type-aliases-package: org.owls.mybatis.model
type-handlers-package: org.owls.mybatis.handler
mapper-locations: classpath:/resources/mybatis/mapper/*_mapper.xml
configuration:
map-underscore-to-camel-case: true
default-fetch-size: 100
default-statement-timeout: 30
А это /resources/mybatis/mapper/test_mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.owls.mybatis.mapper.TestMapper">
<select id="selectTimestamp" resultType="map">
SELECT NOW() FROM DUAL
</select>
</mapper>
Отсюда Java-коды.
Во-первых, интерфейс Mapper TestMapper
который в org.owls.mybatis.mapper
пакет
public interface TestMapper {
@Select("SELECT NOW() FROM DUAL")
public Map selectTimestamp() throws Exception;
}
Во-вторых, я сделал сервис, который генерирует SqlSessionTemplate
, Вот код
@Service
public class MybatisService implements ApplicationContextAware {
private Logger logger = Logger.getLogger(MybatisService.class);
private ApplicationContext context;
@Autowired
DBService dbService;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
// Register a Object with a name, if not exists
private void registerBean(String beanName, Object newBean){
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(newBean.getClass());
BeanDefinition def = builder.getBeanDefinition();
if(!registry.containsBeanDefinition(beanName)) registry.registerBeanDefinition(beanName, def);
}
/*
* Managing SqlSessionFactory
* */
private SqlSessionFactory generateSqlSessionFactory(String dsId, DataSource ds) throws Exception {
String beanName = dsId + "SqlSessionFactory";
SqlSessionFactory sqlSessionFactory = null;
try {
Object registeredBean = context.getBean(beanName);
logger.info("Found SqlSessionFactory bean [ " + beanName + " ]. Returns the instance");
sqlSessionFactory = (SqlSessionFactory) registeredBean;
} catch (NoSuchBeanDefinitionException e) {
logger.info("Not found SqlSessionFactory bean [ " + beanName + " ]. Generate new one");
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(ds);
sqlSessionFactory = (SqlSessionFactory) sqlSessionFactoryBean.getObject();
registerBean(beanName, sqlSessionFactory);
}
return sqlSessionFactory;
}
/*
* Managing SqlSessionTemplate
* */
private SqlSessionTemplate generateSqlSessionTemplate(String dsId, SqlSessionFactory sqlSessionFactory) throws Exception {
String beanName = dsId + "SqlSessionTemplate";
SqlSessionTemplate sqlSessionTemplate = null;
try {
Object registeredBean = context.getBean(beanName);
logger.info("Found SqlSessionTemplate bean [ " + beanName + " ]. Returns the instance");
sqlSessionTemplate = (SqlSessionTemplate) registeredBean;
} catch (NoSuchBeanDefinitionException e) {
logger.info("Not found SqlSessionTemplate bean [ " + beanName + " ]. Generate new one");
sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
registerBean(beanName, sqlSessionFactory);
}
return sqlSessionTemplate;
}
public SqlSessionTemplate getSqlSessionTemplate(String dsId) {
SqlSessionTemplate template = null;
try {
DataSource ds = dbService.getDS(dsId);
SqlSessionFactory sqlSessionFactory = generateSqlSessionFactory(dsId, ds);
template = generateSqlSessionTemplate(dsId, sqlSessionFactory);
} catch (Exception e) {
logger.error("Failed to fetch SqlSessionFactory. returns null value ", e);
}
return template;
}
}
Наконец, в контроллере, я называю это с...
@Controller
@RequestMapping(value = {"/test"})
public class TestController {
@Autowired
MybatisService mybatisService;
private SqlSessionTemplate sqlSessionTemplate;
private Logger logger = Logger.getLogger(TestController.class);
@PostConstruct
public void init(){
sqlSessionTemplate = mybatisService.getSqlSessionTemplate("test");
}
@RequestMapping(value = {"getCurrentTimestamp"})
public @ResponseBody String getCurrentTimestamp() throws Exception {
logger.info("=== SqlSessionTemplate Info in Controller :: " + sqlSessionTemplate);
Map<String, Object> data = (Map) sqlSessionTemplate.selectOne("org.owls.mybatis.mapper.TestMapper.selectTimestamp");
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(data);
}
};
Я думаю, что сообщение об ошибке немного расплывчато. (Я не мог найти подсказку, которая является неправильной среди настроек - mapper xml
, application.yml
, db connection
)
Спасибо за ответы. Доступ к полному исходному коду
======================== Редактировать #1. 19 Фев =============================
Я узнал, что SqlSessionFactory
Configuration
не имеет никакого картографа вообще.
Я мог бы распечатать список картографов следующим образом. В коде где генерирует SqlSessionFactory
...
// FOR TEST 0219
MapperRegistry registry = sqlSessionFactory.getConfiguration().getMapperRegistry();
logger.info("=== Printing Mapper Registry starts");
registry.getMappers().forEach(mapper -> {
logger.info("Mapper Registry :: " + mapper.getName());
});
logger.info("=== Printing Mapper Registry ends");
// FOR TEST 0219
В моем случае он ничего не печатает, но может добавить маппер или пакет через MapperRegistry
пример.
Вот что я думал
SqlSessionFactory
(предоставлено ibatis) требуетDataSource
SqlSessionTemplate
(предоставлено ibatis) является конечной точкой и требуетSqlSessionFactory
- В заключение,
SqlSessionTemplate
зависит отSqlSessionFactory
который зависит отDataSource
,
Если так, то как я мог правильно генерировать SqlSession*
как бин в проекте, который использует динамический источник данных?
В моем верхнем коде (отредактированном) я смог добавить маппер как бин, но каждый SqlSessionFactory
имеет те же Mappers(дублируется, так как MapperRegistry
получить имя пакета в качестве параметра)
Как я могу установить разные сопоставители для соответствующих SqlSessionFactory
?