这里,首先想说的是,现在的web应用,处理的数据对象,有结构化的,也有非结构化的。同时存在。但是在spring-boot操作数据库的时候,若是在properties文件中配置数据源的信息,通过默认配置加载数据源的话,往往只会启动一个。
我出于想弄清如何配置数据源的目的,在这里demo一个配置两个数据源的例子。分别是mysql和mongo。mysql的持久化采用的是mybatis。
mongo的操作比较简单,直接贴上配置数据库的代码:
1 package com.shihuc.dbconn.sourceconfig.mongo; 2 3 import java.util.Arrays; 4 5 import org.springframework.beans.factory.annotation.Value; 6 import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; 8 import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; 9 import org.springframework.context.annotation.Bean; 10 import org.springframework.context.annotation.ComponentScan; 11 import org.springframework.context.annotation.Configuration; 12 import org.springframework.data.mongodb.config.AbstractMongoConfiguration; 13 import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; 14 15 import com.mongodb.Mongo; 16 import com.mongodb.MongoClient; 17 import com.mongodb.MongoCredential; 18 import com.mongodb.ServerAddress; 19 import com.mongodb.WriteConcern; 20 21 @Configuration 22 @EnableAutoConfiguration(exclude={MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) 23 @ComponentScan 24 @EnableMongoRepositories 25 public class MongoDataSourceConfig extends AbstractMongoConfiguration{ 26 27 @Value("${mongo.database}") 28 private String dbname; 29 30 @Value("${mongo.host}") 31 private String dbhost; 32 33 @Value("${mongo.port}") 34 private String dbport; 35 36 @Value("${mongo.username}") 37 private String username; 38 39 @Value("${mongo.password}") 40 private String password; 41 42 @Override 43 protected String getDatabaseName() { 44 return this.dbname; 45 } 46 47 public MongoDataSourceConfig(){ 48 if(null == dbport || "".equalsIgnoreCase(dbport.trim())){ 49 dbport = "27017"; 50 } 51 } 52 53 @Override 54 @Bean(name = "mongods") 55 public Mongo mongo() throws Exception { 56 ServerAddress serverAdress = new ServerAddress(dbhost, Integer.valueOf(dbport)); 57 MongoCredential credential = MongoCredential.createMongoCRCredential(username, dbname , password.toCharArray()); 58 //Do not use new Mongo(), is deprecated. 59 Mongo mongo = new MongoClient(serverAdress, Arrays.asList(credential)); 60 mongo.setWriteConcern(WriteConcern.SAFE); 61 return mongo; 62 } 63 }
mongo数据库配置继承AbstractMongoConfiguration,在这个过程中,会向spring容器注册一个mongoTemplate,这个很重要,后期操作mongo数据库时,主要靠它。
这里重点说下spring-boot和mybatis集成操作mysql的配置和注意事项。
1 package com.shihuc.dbconn.sourceconfig.mysql; 2 3 import javax.sql.DataSource; 4 5 import org.springframework.beans.factory.annotation.Value; 6 import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Configuration; 10 import org.springframework.jdbc.datasource.DriverManagerDataSource; 11 12 @Configuration 13 @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) 14 public class MysqlDataSourceConfig { 15 16 @Value("${mysql.driver}") 17 private String driver; 18 19 @Value("${mysql.url}") 20 private String url; 21 22 @Value("${mysql.username}") 23 private String username; 24 25 @Value("${mysql.password}") 26 private String password; 27 28 @Bean(name="mysqlds") 29 public DataSource mysql() 30 { 31 DriverManagerDataSource ds = new DriverManagerDataSource(); 32 ds.setDriverClassName(driver); 33 ds.setUrl(url); 34 ds.setUsername(username); 35 ds.setPassword(password); 36 return ds; 37 } 38 }
这个是datasource的配置,注意,类似mongo的配置,要将自动配置的类给exclude掉。让spring只处理我们希望的数据源。否则会受到classpath下的信息,干扰数据源的配置。
另外,就是mybatis的配置。由于spring-boot重点特色是纯java config,所以,这里也采用java配置和注解启用mybatis。
1 package com.shihuc.dbconn.sourceconfig.mysql; 2 3 import javax.sql.DataSource; 4 5 import org.apache.ibatis.session.SqlSessionFactory; 6 import org.mybatis.spring.SqlSessionFactoryBean; 7 import org.mybatis.spring.annotation.MapperScan; 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.beans.factory.annotation.Qualifier; 10 import org.springframework.context.annotation.Bean; 11 import org.springframework.context.annotation.Configuration; 12 13 @Configuration 14 @MapperScan(basePackages = "com.shihuc.dbconn.dao.mysql") 15 public class MysqlMybatisConfig { 16 17 @Autowired 18 @Qualifier("mysqlds") 19 private DataSource mysqlds; 20 21 @Bean 22 public SqlSessionFactory sqlSessionFactoryBean() throws Exception { 23 final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); 24 sessionFactory.setDataSource(mysqlds); 25 return sessionFactory.getObject(); 26 } 27 }
这里,mapperscan主要是为配置JavaBean和持久化对象之间的映射关系设定根路径。在这个例子中,mapper部分,其实就是通过insert,delete,update,select等注解,结合具体的SQL语句实现ORM的关系。看看这里的代码:
1 package com.shihuc.dbconn.dao.mysql; 2 3 import org.apache.ibatis.annotations.Insert; 4 import org.apache.ibatis.annotations.Select; 5 6 import com.shihuc.dbconn.pojo.mysql.MysqlUser; 7 8 public interface IMysqlUser { 9 10 @Select("SELECT * FROM user WHERE id = #{userId}") 11 public MysqlUser getUser(int userId); 12 13 @Insert("insert into user (username, job, age, hometown) values(#{username}, #{job}, #{age}, #{hometown})") 14 public int addUser(MysqlUser user); 15 }
另外,配合这个IMysqlUser使用的MysqlUser的定义也很重要,主要是里面的constructor函数,通常,添加数据,比如上面的addUser操作,是要创建一个POJO的对象,为了方便,一般都会创建一个带参数的构造函数,注意,必须也同时创建一个无参数的构造函数,否则,在做查询操作,比如上面的getUser时,会出现下面的错误:
1 . ____ _ __ _ _ 2 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ 3 ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ 4 \\/ ___)| |_)| | | | | || (_| | ) ) ) ) 5 ' |____| .__|_| |_|_| |_\__, | / / / / 6 =========|_|==============|___/=/_/_/_/ 7 :: Spring Boot :: (v1.2.7.RELEASE) 8 9 2016-01-29 15:01:07.930 INFO 30880 --- [ main] com.shihuc.dbconn.DbConnApp : Starting DbConnApp on CloudGame with PID 30880 (/home/webWps/dbconn/target/classes started by root in /home/webWps/dbconn) 10 2016-01-29 15:01:07.997 INFO 30880 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@a85aa40: startup date [Fri Jan 29 15:01:07 CST 2016]; root of context hierarchy 11 2016-01-29 15:01:09.887 INFO 30880 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'mysqlDataSourceConfig' of type [class com.shihuc.dbconn.sourceconfig.mysql.MysqlDataSourceConfig$$EnhancerBySpringCGLIB$$63c6820a] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 12 2016-01-29 15:01:09.915 INFO 30880 --- [ main] o.s.j.d.DriverManagerDataSource : Loaded JDBC driver: com.mysql.jdbc.Driver 13 2016-01-29 15:01:09.923 INFO 30880 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'mysqlds' of type [class org.springframework.jdbc.datasource.DriverManagerDataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 14 2016-01-29 15:01:09.926 INFO 30880 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'mysqlMybatisConfig' of type [class com.shihuc.dbconn.sourceconfig.mysql.MysqlMybatisConfig$$EnhancerBySpringCGLIB$$bbfde9e4] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 15 2016-01-29 15:01:09.991 INFO 30880 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'sqlSessionFactoryBean' of type [class org.apache.ibatis.session.defaults.DefaultSqlSessionFactory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 16 2016-01-29 15:01:10.040 INFO 30880 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'IMysqlUser' of type [class org.mybatis.spring.mapper.MapperFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 17 2016-01-29 15:01:10.876 INFO 30880 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 18 2016-01-29 15:01:10.888 INFO 30880 --- [ main] com.shihuc.dbconn.DbConnApp : Started DbConnApp in 3.274 seconds (JVM running for 3.561) 19 Exception in thread "main" org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Error instantiating class com.shihuc.dbconn.pojo.mysql.MysqlUser with invalid types () or values (). Cause: java.lang.NoSuchMethodException: com.shihuc.dbconn.pojo.mysql.MysqlUser.<init>() 20 at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:75) 21 at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:371) 22 at com.sun.proxy.$Proxy26.selectOne(Unknown Source) 23 at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:163) 24 at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:63) 25 at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:43) 26 at com.sun.proxy.$Proxy34.getUser(Unknown Source) 27 at com.shihuc.dbconn.service.mysql.MysqlUserService.getUser(MysqlUserService.java:20) 28 at com.shihuc.dbconn.DbConnApp.main(DbConnApp.java:34) 29 Caused by: org.apache.ibatis.reflection.ReflectionException: Error instantiating class com.shihuc.dbconn.pojo.mysql.MysqlUser with invalid types () or values (). Cause: java.lang.NoSuchMethodException: com.shihuc.dbconn.pojo.mysql.MysqlUser.<init>() 30 at org.apache.ibatis.reflection.factory.DefaultObjectFactory.instantiateClass(DefaultObjectFactory.java:83) 31 at org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(DefaultObjectFactory.java:45) 32 at org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(DefaultObjectFactory.java:38) 33 at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createResultObject(DefaultResultSetHandler.java:530) 34 at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createResultObject(DefaultResultSetHandler.java:509) 35 at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:329) 36 at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForSimpleResultMap(DefaultResultSetHandler.java:289) 37 at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:264) 38 at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:234) 39 at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:152) 40 at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:57) 41 at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:70) 42 at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:57) 43 at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:259) 44 at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:132) 45 at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:105) 46 at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:81) 47 at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104) 48 at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98) 49 at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:62) 50 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 51 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 52 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 53 at java.lang.reflect.Method.invoke(Method.java:606) 54 at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358) 55 ... 7 more 56 Caused by: java.lang.NoSuchMethodException: com.shihuc.dbconn.pojo.mysql.MysqlUser.<init>() 57 at java.lang.Class.getConstructor0(Class.java:2892) 58 at java.lang.Class.getDeclaredConstructor(Class.java:2058) 59 at org.apache.ibatis.reflection.factory.DefaultObjectFactory.instantiateClass(DefaultObjectFactory.java:57) 60 ... 31 more 61 2016-01-29 15:01:11.139 INFO 30880 --- [ Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@a85aa40: startup date [Fri Jan 29 15:01:07 CST 2016]; root of context hierarchy 62 2016-01-29 15:01:11.142 INFO 30880 --- [ Thread-1] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown