getResolvedReadDataSources() {
33 | return resolvedReadDataSources;
34 | }
35 |
36 | public int getReadDsSize() {
37 | return readDsSize;
38 | }
39 |
40 | public void setReadDataSoures(List readDataSoures) {
41 | this.readDataSoures = readDataSoures;
42 | }
43 |
44 | public void setWriteDataSource(Object writeDataSource) {
45 | this.writeDataSource = writeDataSource;
46 | }
47 | /**
48 | * Set the DataSourceLookup implementation to use for resolving data source
49 | * name Strings in the {@link #setTargetDataSources targetDataSources} map.
50 | * Default is a {@link JndiDataSourceLookup}, allowing the JNDI names
51 | * of application server DataSources to be specified directly.
52 | */
53 | public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
54 | this.dataSourceLookup = (dataSourceLookup != null ? dataSourceLookup : new JndiDataSourceLookup());
55 | }
56 |
57 |
58 | @Override
59 | public void afterPropertiesSet() {
60 |
61 | if (writeDataSource == null) {
62 | throw new IllegalArgumentException("Property 'writeDataSource' is required");
63 | }
64 | this.resolvedWriteDataSource = resolveSpecifiedDataSource(writeDataSource);
65 |
66 | if (this.readDataSoures == null || this.readDataSoures.size() ==0) {
67 | throw new IllegalArgumentException("Property 'resolvedReadDataSources' is required");
68 | }
69 | resolvedReadDataSources = new ArrayList(readDataSoures.size());
70 | for (Object item : readDataSoures) {
71 | resolvedReadDataSources.add(resolveSpecifiedDataSource(item));
72 | }
73 | readDsSize = readDataSoures.size();
74 | }
75 |
76 | /**
77 | * Resolve the specified data source object into a DataSource instance.
78 | * The default implementation handles DataSource instances and data source
79 | * names (to be resolved via a {@link #setDataSourceLookup DataSourceLookup}).
80 | * @param dataSource the data source value object as specified in the
81 | * {@link #setTargetDataSources targetDataSources} map
82 | * @return the resolved DataSource (never {@code null})
83 | * @throws IllegalArgumentException in case of an unsupported value type
84 | */
85 | protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
86 | if (dataSource instanceof DataSource) {
87 | return (DataSource) dataSource;
88 | }
89 | else if (dataSource instanceof String) {
90 | return this.dataSourceLookup.getDataSource((String) dataSource);
91 | }
92 | else {
93 | throw new IllegalArgumentException(
94 | "Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
95 | }
96 | }
97 | @Override
98 | public DataSource getTargetDataSource() {
99 | if (ConnectionHold.WRITE.equals(ConnectionHold.CURRENT_CONNECTION.get())) {
100 | return resolvedWriteDataSource;
101 | } else {
102 | return loadBalance();
103 | }
104 | }
105 |
106 | protected abstract DataSource loadBalance();
107 | }
108 |
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/spring-boot-mybatis-rw-starter/src/main/java/com/github/chenlei2/springboot/mybatis/rw/starter/MybatisProperties.java:
--------------------------------------------------------------------------------
1 | package com.github.chenlei2.springboot.mybatis.rw.starter;
2 |
3 | import java.io.IOException;
4 | import java.util.ArrayList;
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | import org.apache.ibatis.session.Configuration;
9 | import org.apache.ibatis.session.ExecutorType;
10 | import org.springframework.boot.context.properties.ConfigurationProperties;
11 | import org.springframework.boot.context.properties.NestedConfigurationProperty;
12 | import org.springframework.core.io.Resource;
13 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
14 | import org.springframework.core.io.support.ResourcePatternResolver;
15 |
16 | /**
17 | * Configuration properties for Mybatis.
18 | *
19 | */
20 | @ConfigurationProperties(prefix = "spring.data.mybatis")
21 | public class MybatisProperties {
22 |
23 | /**
24 | * Config file path.
25 | */
26 | private String configLocation;
27 |
28 | /**
29 | * Location of mybatis mapper files.
30 | */
31 | private String[] mapperLocations;
32 |
33 | /**
34 | * Package to scan domain objects.
35 | */
36 | private String typeAliasesPackage;
37 |
38 | /**
39 | * Package to scan handlers.
40 | */
41 | private String typeHandlersPackage;
42 |
43 | /**
44 | * Check the config file exists.
45 | */
46 | private boolean checkConfigLocation = false;
47 |
48 | /**
49 | * Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.
50 | */
51 | private ExecutorType executorType;
52 |
53 | /**
54 | * A Configuration object for customize default settings. If
55 | * {@link #configLocation} is specified, this property is not used.
56 | */
57 | @NestedConfigurationProperty
58 | private Configuration configuration;
59 |
60 | /**
61 | * @since 1.1.0
62 | * @return
63 | */
64 | public String getConfigLocation() {
65 | return this.configLocation;
66 | }
67 |
68 | /**
69 | * @since 1.1.0
70 | * @return
71 | */
72 | public void setConfigLocation(String configLocation) {
73 | this.configLocation = configLocation;
74 | }
75 |
76 | @Deprecated
77 | public String getConfig() {
78 | return this.configLocation;
79 | }
80 |
81 | @Deprecated
82 | public void setConfig(String config) {
83 | this.configLocation = config;
84 | }
85 |
86 | public String[] getMapperLocations() {
87 | return this.mapperLocations;
88 | }
89 |
90 | public void setMapperLocations(String[] mapperLocations) {
91 | this.mapperLocations = mapperLocations;
92 | }
93 |
94 | public String getTypeHandlersPackage() {
95 | return this.typeHandlersPackage;
96 | }
97 |
98 | public void setTypeHandlersPackage(String typeHandlersPackage) {
99 | this.typeHandlersPackage = typeHandlersPackage;
100 | }
101 |
102 | public String getTypeAliasesPackage() {
103 | return this.typeAliasesPackage;
104 | }
105 |
106 | public void setTypeAliasesPackage(String typeAliasesPackage) {
107 | this.typeAliasesPackage = typeAliasesPackage;
108 | }
109 |
110 | public boolean isCheckConfigLocation() {
111 | return this.checkConfigLocation;
112 | }
113 |
114 | public void setCheckConfigLocation(boolean checkConfigLocation) {
115 | this.checkConfigLocation = checkConfigLocation;
116 | }
117 |
118 | public ExecutorType getExecutorType() {
119 | return this.executorType;
120 | }
121 |
122 | public void setExecutorType(ExecutorType executorType) {
123 | this.executorType = executorType;
124 | }
125 |
126 | public Configuration getConfiguration() {
127 | return configuration;
128 | }
129 |
130 | public void setConfiguration(Configuration configuration) {
131 | this.configuration = configuration;
132 | }
133 |
134 | public Resource[] resolveMapperLocations() {
135 | ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
136 | List resources = new ArrayList();
137 | if (this.mapperLocations != null) {
138 | for (String mapperLocation : this.mapperLocations) {
139 | try {
140 | Resource[] mappers = resourceResolver.getResources(mapperLocation);
141 | resources.addAll(Arrays.asList(mappers));
142 | } catch (IOException e) {
143 | // ignore
144 | }
145 | }
146 | }
147 | return resources.toArray(new Resource[resources.size()]);
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/spring-boot-mybatis-rw-starter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.github.chenlei2
8 | spring-boot-mybatis-rw
9 | 0.0.1
10 |
11 | com.github.chenlei2
12 | spring-boot-mybatis-rw-starter
13 | spring-boot-mybatis-rw-starter
14 | https://github.com/chenlei2/spring-boot-mybatis-rw/tree/master
15 |
16 | 基于mybatis,springboot开箱即用的读写分离插件
17 |
18 |
19 | The Apache Software License, Version 2.0
20 | http://www.apache.org/licenses/LICENSE-2.0.txt
21 |
22 |
23 |
24 |
25 |
26 | chenlei2
27 | 15184463252@qq.com
28 |
29 |
30 |
31 |
32 | scm:git:https://github.com/chenlei2/spring-boot-mybatis-rw.git
33 | scm:git:https://github.com/chenlei2/spring-boot-mybatis-rw.git
34 | https://github.com/chenlei2/spring-boot-mybatis-rw/tree/master
35 |
36 |
37 |
38 | UTF-8
39 | 1.8
40 |
41 |
42 |
43 | org.mybatis
44 | mybatis
45 |
46 |
47 | org.mybatis
48 | mybatis-spring
49 |
50 |
51 | org.springframework.boot
52 | spring-boot-starter-jdbc
53 |
54 |
55 |
56 |
57 |
58 | release
59 |
60 |
61 |
62 |
63 | org.apache.maven.plugins
64 | maven-source-plugin
65 | 3.0.1
66 |
67 |
68 | package
69 |
70 | jar-no-fork
71 |
72 |
73 |
74 |
75 |
76 |
77 | org.apache.maven.plugins
78 | maven-gpg-plugin
79 | 1.6
80 |
81 |
82 | verify
83 |
84 | sign
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | oss
94 | https://oss.sonatype.org/content/repositories/snapshots/
95 |
96 |
97 | oss
98 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/spring-boot-mybatis-rw-starter/src/main/java/com/github/chenlei2/springboot/mybatis/rw/starter/pulgin/RWPlugin.java:
--------------------------------------------------------------------------------
1 | package com.github.chenlei2.springboot.mybatis.rw.starter.pulgin;
2 |
3 | import java.lang.reflect.InvocationHandler;
4 | import java.lang.reflect.Proxy;
5 | import java.sql.Connection;
6 | import java.util.Properties;
7 |
8 | import com.github.chenlei2.springboot.mybatis.rw.starter.datasource.ConnectionHold;
9 | import org.apache.commons.logging.Log;
10 | import org.apache.commons.logging.LogFactory;
11 | import org.apache.ibatis.executor.statement.RoutingStatementHandler;
12 | import org.apache.ibatis.executor.statement.StatementHandler;
13 | import org.apache.ibatis.logging.jdbc.ConnectionLogger;
14 | import org.apache.ibatis.mapping.MappedStatement;
15 | import org.apache.ibatis.plugin.Interceptor;
16 | import org.apache.ibatis.plugin.Intercepts;
17 | import org.apache.ibatis.plugin.Invocation;
18 | import org.apache.ibatis.plugin.Plugin;
19 | import org.apache.ibatis.plugin.Signature;
20 | import org.apache.ibatis.reflection.DefaultReflectorFactory;
21 | import org.apache.ibatis.reflection.MetaObject;
22 | import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
23 | import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
24 | import org.springframework.jdbc.datasource.ConnectionProxy;
25 |
26 | /**
27 | * 数据源读写分离路由
28 | *
29 | * @author chenlei
30 | *
31 | */
32 | @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) })
33 | public class RWPlugin implements Interceptor {
34 |
35 | public static final Log LOG = LogFactory.getLog(RWPlugin.class);
36 |
37 | public Object intercept(Invocation invocation) throws Throwable {
38 |
39 | Connection conn = (Connection) invocation.getArgs()[0];
40 | conn = unwrapConnection(conn);
41 | if (conn instanceof ConnectionProxy) {
42 | //强制走写库
43 | if(ConnectionHold.FORCE_WRITE.get() != null && ConnectionHold.FORCE_WRITE.get()){
44 | if (LOG.isDebugEnabled()) {
45 | LOG.debug("本事务强制走写库, 数据库url"+conn.getMetaData().getURL()+", 数据库name" + conn.getMetaData().getDatabaseProductName());
46 | }
47 | routeConnection(ConnectionHold.WRITE, conn);
48 | return invocation.proceed();
49 | }
50 | StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
51 | MetaObject metaObject = MetaObject.forObject(statementHandler, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
52 | MappedStatement mappedStatement = null;
53 | if (statementHandler instanceof RoutingStatementHandler) {
54 | mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
55 | } else {
56 | mappedStatement = (MappedStatement) metaObject.getValue("mappedStatement");
57 | }
58 | String key = ConnectionHold.WRITE;
59 | String sel = statementHandler.getBoundSql().getSql().trim().substring(0,3);
60 | if (sel.equalsIgnoreCase("sel") && !mappedStatement.getId().endsWith(".insert!selectKey")) {
61 | key = ConnectionHold.READ;
62 | if (LOG.isDebugEnabled()) {
63 | LOG.debug("当前数据库为读库, 数据库url"+conn.getMetaData().getURL()+", 数据库name" + conn.getMetaData().getDatabaseProductName());
64 | }
65 | } else {
66 | if (LOG.isDebugEnabled()) {
67 | LOG.debug("当前数据库为写库, 数据库url"+conn.getMetaData().getURL()+", 数据库name" + conn.getMetaData().getDatabaseProductName());
68 | }
69 | }
70 | routeConnection(key, conn);
71 | }
72 |
73 | return invocation.proceed();
74 |
75 | }
76 |
77 | private void routeConnection(String key, Connection conn) {
78 | ConnectionHold.CURRENT_CONNECTION.set(key);
79 | // 同一个线程下保证最多只有一个写数据链接和读数据链接
80 | if (!ConnectionHold.CONNECTION_CONTEXT.get().containsKey(key)) {
81 | ConnectionProxy conToUse = (ConnectionProxy) conn;
82 | conn = conToUse.getTargetConnection();
83 | ConnectionHold.CONNECTION_CONTEXT.get().put(key, conn);
84 | }
85 | }
86 |
87 | public Object plugin(Object target) {
88 | if (target instanceof StatementHandler) {
89 | return Plugin.wrap(target, this);
90 | } else {
91 | return target;
92 | }
93 | }
94 |
95 | public void setProperties(Properties properties) {
96 | // NOOP
97 |
98 | }
99 | /**
100 | * MyBatis wraps the JDBC Connection with a logging proxy but Spring registers the original connection so it should
101 | * be unwrapped before calling {@code DataSourceUtils.isConnectionTransactional(Connection, DataSource)}
102 | *
103 | * @param connection May be a {@code ConnectionLogger} proxy
104 | * @return the original JDBC {@code Connection}
105 | */
106 | private Connection unwrapConnection(Connection connection) {
107 | if (Proxy.isProxyClass(connection.getClass())) {
108 | InvocationHandler handler = Proxy.getInvocationHandler(connection);
109 | if (handler instanceof ConnectionLogger) {
110 | return ((ConnectionLogger) handler).getConnection();
111 | }
112 | }
113 | return connection;
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/spring-boot-mybatis-rw-starter/src/main/java/com/github/chenlei2/springboot/mybatis/rw/starter/MybatisAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.github.chenlei2.springboot.mybatis.rw.starter;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | import javax.annotation.PostConstruct;
9 |
10 | import org.apache.ibatis.mapping.DatabaseIdProvider;
11 | import org.apache.ibatis.plugin.Interceptor;
12 | import org.apache.ibatis.session.ExecutorType;
13 | import org.apache.ibatis.session.SqlSessionFactory;
14 | import org.apache.tomcat.jdbc.pool.DataSource;
15 | import org.mybatis.spring.SqlSessionFactoryBean;
16 | import org.mybatis.spring.SqlSessionTemplate;
17 | import com.github.chenlei2.springboot.mybatis.rw.starter.datasource.DataSourceProxy;
18 | import com.github.chenlei2.springboot.mybatis.rw.starter.datasource.DataSourceRout;
19 | import com.github.chenlei2.springboot.mybatis.rw.starter.datasource.impl.RoundRobinRWDataSourceRout;
20 | import com.github.chenlei2.springboot.mybatis.rw.starter.pulgin.RWPlugin;
21 | import org.springframework.beans.factory.annotation.Autowired;
22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24 | import org.springframework.boot.context.properties.ConfigurationProperties;
25 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
26 | import org.springframework.context.annotation.Bean;
27 | import org.springframework.context.annotation.Configuration;
28 | import org.springframework.core.io.DefaultResourceLoader;
29 | import org.springframework.core.io.Resource;
30 | import org.springframework.core.io.ResourceLoader;
31 | import org.springframework.jdbc.datasource.DataSourceTransactionManager;
32 | import org.springframework.util.Assert;
33 | import org.springframework.util.ObjectUtils;
34 | import org.springframework.util.StringUtils;
35 |
36 | /**
37 | *
38 | * @author chenlei
39 | */
40 | @Configuration()
41 | @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
42 | @EnableConfigurationProperties(MybatisProperties.class)
43 | @ConfigurationProperties("spring.mybatis.rw")
44 | public class MybatisAutoConfiguration {
45 |
46 | @Autowired
47 | private MybatisProperties properties;
48 |
49 | @Autowired(required = false)
50 | private Interceptor[] interceptors;
51 | @Autowired
52 | private ResourceLoader resourceLoader = new DefaultResourceLoader();
53 |
54 | @Autowired(required = false)
55 | private DatabaseIdProvider databaseIdProvider;
56 |
57 | private List readDataSources = new ArrayList<>();
58 |
59 | @PostConstruct
60 | public void checkConfigFileExists() {
61 | if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
62 | Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
63 | Assert.state(resource.exists(), "Cannot find config location: " + resource
64 | + " (please add config file or check your Mybatis configuration)");
65 | }
66 | }
67 |
68 | @Bean
69 | public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSource)
70 | throws Exception {
71 |
72 | SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
73 | Interceptor rwplugin = new RWPlugin();
74 | if (StringUtils.hasText(this.properties.getConfigLocation())) {
75 | factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
76 | }
77 | factory.setConfiguration(properties.getConfiguration());
78 |
79 | if (ObjectUtils.isEmpty(this.interceptors)) {
80 | Interceptor[] plugins = { rwplugin };
81 | factory.setPlugins(plugins);
82 | } else {
83 | List interceptorList = new ArrayList(Arrays.asList(interceptors));
84 | interceptorList.add(rwplugin);
85 | factory.setPlugins(interceptorList.toArray(new Interceptor[interceptorList.size()]));
86 | }
87 | if (this.databaseIdProvider != null) {
88 | factory.setDatabaseIdProvider(this.databaseIdProvider);
89 | }
90 | if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
91 | factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
92 | }
93 | if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
94 | factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
95 | }
96 | if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
97 | factory.setMapperLocations(this.properties.resolveMapperLocations());
98 | }
99 | factory.setDataSource(dataSource);
100 | return factory.getObject();
101 | }
102 |
103 |
104 | @Bean
105 | @ConditionalOnMissingBean
106 | public DataSourceRout readRoutingDataSource() {
107 | RoundRobinRWDataSourceRout proxy = new RoundRobinRWDataSourceRout();
108 | proxy.setReadDataSoures(getReadDataSources());
109 | proxy.setWriteDataSource(writeDataSource());
110 | return proxy;
111 | }
112 |
113 |
114 |
115 | public List getReadDataSources() {
116 | return readDataSources;
117 | }
118 | @ConfigurationProperties("spring.mybatis.rw.writeDataSource")
119 | @Bean
120 | public DataSource writeDataSource() {
121 | return new DataSource();
122 | }
123 |
124 | @Bean
125 | public DataSourceProxy dataSource(DataSourceRout dataSourceRout) {
126 | return new DataSourceProxy(dataSourceRout);
127 | }
128 |
129 | @Bean
130 | public DataSourceTransactionManager transactionManager(DataSourceProxy DataSource) {
131 | return new DataSourceTransactionManager(DataSource);
132 | }
133 |
134 | @Bean
135 | public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
136 | ExecutorType executorType = this.properties.getExecutorType();
137 | if (executorType != null) {
138 | return new SqlSessionTemplate(sqlSessionFactory, executorType);
139 | } else {
140 | return new SqlSessionTemplate(sqlSessionFactory);
141 | }
142 | }
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/mybatis-rw-sample-xml/src/main/java/com/github/chenlei2/springboot/mybatis/rw/sample/mapper/StudentsMapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | and ${criterion.condition}
17 |
18 |
19 | and ${criterion.condition} #{criterion.value}
20 |
21 |
22 | and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
23 |
24 |
25 | and ${criterion.condition}
26 |
27 | #{listItem}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | and ${criterion.condition}
46 |
47 |
48 | and ${criterion.condition} #{criterion.value}
49 |
50 |
51 | and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
52 |
53 |
54 | and ${criterion.condition}
55 |
56 | #{listItem}
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | id, name
68 |
69 |
83 |
89 |
90 | delete from students
91 | where id = #{id,jdbcType=BIGINT}
92 |
93 |
94 | delete from students
95 |
96 |
97 |
98 |
99 |
100 | insert into students (id, name)
101 | values (#{id,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR})
102 |
103 |
104 | insert into students
105 |
106 |
107 | id,
108 |
109 |
110 | name,
111 |
112 |
113 |
114 |
115 | #{id,jdbcType=BIGINT},
116 |
117 |
118 | #{name,jdbcType=VARCHAR},
119 |
120 |
121 |
122 |
128 |
129 | update students
130 |
131 |
132 | id = #{record.id,jdbcType=BIGINT},
133 |
134 |
135 | name = #{record.name,jdbcType=VARCHAR},
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | update students
144 | set id = #{record.id,jdbcType=BIGINT},
145 | name = #{record.name,jdbcType=VARCHAR}
146 |
147 |
148 |
149 |
150 |
151 | update students
152 |
153 |
154 | name = #{name,jdbcType=VARCHAR},
155 |
156 |
157 | where id = #{id,jdbcType=BIGINT}
158 |
159 |
160 | update students
161 | set name = #{name,jdbcType=VARCHAR}
162 | where id = #{id,jdbcType=BIGINT}
163 |
164 |
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/spring-boot-mybatis-rw-sample/src/main/java/com/github/chenlei2/springboot/mybatis/rw/sample/mapper/StudentsMapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | and ${criterion.condition}
17 |
18 |
19 | and ${criterion.condition} #{criterion.value}
20 |
21 |
22 | and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
23 |
24 |
25 | and ${criterion.condition}
26 |
27 | #{listItem}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | and ${criterion.condition}
46 |
47 |
48 | and ${criterion.condition} #{criterion.value}
49 |
50 |
51 | and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
52 |
53 |
54 | and ${criterion.condition}
55 |
56 | #{listItem}
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | id, name
68 |
69 |
83 |
89 |
90 | delete from students
91 | where id = #{id,jdbcType=BIGINT}
92 |
93 |
94 | delete from students
95 |
96 |
97 |
98 |
99 |
100 | insert into students (id, name)
101 | values (#{id,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR})
102 |
103 |
104 | insert into students
105 |
106 |
107 | id,
108 |
109 |
110 | name,
111 |
112 |
113 |
114 |
115 | #{id,jdbcType=BIGINT},
116 |
117 |
118 | #{name,jdbcType=VARCHAR},
119 |
120 |
121 |
122 |
128 |
129 | update students
130 |
131 |
132 | id = #{record.id,jdbcType=BIGINT},
133 |
134 |
135 | name = #{record.name,jdbcType=VARCHAR},
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | update students
144 | set id = #{record.id,jdbcType=BIGINT},
145 | name = #{record.name,jdbcType=VARCHAR}
146 |
147 |
148 |
149 |
150 |
151 | update students
152 |
153 |
154 | name = #{name,jdbcType=VARCHAR},
155 |
156 |
157 | where id = #{id,jdbcType=BIGINT}
158 |
159 |
160 | update students
161 | set name = #{name,jdbcType=VARCHAR}
162 | where id = #{id,jdbcType=BIGINT}
163 |
164 |
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/mybatis-rw-sample-xml/src/main/java/com/github/chenlei2/springboot/mybatis/rw/sample/mapper/StudentsExample.java:
--------------------------------------------------------------------------------
1 | package com.github.chenlei2.springboot.mybatis.rw.sample.mapper;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class StudentsExample {
7 | protected String orderByClause;
8 |
9 | protected boolean distinct;
10 |
11 | protected List oredCriteria;
12 |
13 | public StudentsExample() {
14 | oredCriteria = new ArrayList();
15 | }
16 |
17 | public void setOrderByClause(String orderByClause) {
18 | this.orderByClause = orderByClause;
19 | }
20 |
21 | public String getOrderByClause() {
22 | return orderByClause;
23 | }
24 |
25 | public void setDistinct(boolean distinct) {
26 | this.distinct = distinct;
27 | }
28 |
29 | public boolean isDistinct() {
30 | return distinct;
31 | }
32 |
33 | public List getOredCriteria() {
34 | return oredCriteria;
35 | }
36 |
37 | public void or(Criteria criteria) {
38 | oredCriteria.add(criteria);
39 | }
40 |
41 | public Criteria or() {
42 | Criteria criteria = createCriteriaInternal();
43 | oredCriteria.add(criteria);
44 | return criteria;
45 | }
46 |
47 | public Criteria createCriteria() {
48 | Criteria criteria = createCriteriaInternal();
49 | if (oredCriteria.size() == 0) {
50 | oredCriteria.add(criteria);
51 | }
52 | return criteria;
53 | }
54 |
55 | protected Criteria createCriteriaInternal() {
56 | Criteria criteria = new Criteria();
57 | return criteria;
58 | }
59 |
60 | public void clear() {
61 | oredCriteria.clear();
62 | orderByClause = null;
63 | distinct = false;
64 | }
65 |
66 | protected abstract static class GeneratedCriteria {
67 | protected List criteria;
68 |
69 | protected GeneratedCriteria() {
70 | super();
71 | criteria = new ArrayList();
72 | }
73 |
74 | public boolean isValid() {
75 | return criteria.size() > 0;
76 | }
77 |
78 | public List getAllCriteria() {
79 | return criteria;
80 | }
81 |
82 | public List getCriteria() {
83 | return criteria;
84 | }
85 |
86 | protected void addCriterion(String condition) {
87 | if (condition == null) {
88 | throw new RuntimeException("Value for condition cannot be null");
89 | }
90 | criteria.add(new Criterion(condition));
91 | }
92 |
93 | protected void addCriterion(String condition, Object value, String property) {
94 | if (value == null) {
95 | throw new RuntimeException("Value for " + property + " cannot be null");
96 | }
97 | criteria.add(new Criterion(condition, value));
98 | }
99 |
100 | protected void addCriterion(String condition, Object value1, Object value2, String property) {
101 | if (value1 == null || value2 == null) {
102 | throw new RuntimeException("Between values for " + property + " cannot be null");
103 | }
104 | criteria.add(new Criterion(condition, value1, value2));
105 | }
106 |
107 | public Criteria andIdIsNull() {
108 | addCriterion("id is null");
109 | return (Criteria) this;
110 | }
111 |
112 | public Criteria andIdIsNotNull() {
113 | addCriterion("id is not null");
114 | return (Criteria) this;
115 | }
116 |
117 | public Criteria andIdEqualTo(Long value) {
118 | addCriterion("id =", value, "id");
119 | return (Criteria) this;
120 | }
121 |
122 | public Criteria andIdNotEqualTo(Long value) {
123 | addCriterion("id <>", value, "id");
124 | return (Criteria) this;
125 | }
126 |
127 | public Criteria andIdGreaterThan(Long value) {
128 | addCriterion("id >", value, "id");
129 | return (Criteria) this;
130 | }
131 |
132 | public Criteria andIdGreaterThanOrEqualTo(Long value) {
133 | addCriterion("id >=", value, "id");
134 | return (Criteria) this;
135 | }
136 |
137 | public Criteria andIdLessThan(Long value) {
138 | addCriterion("id <", value, "id");
139 | return (Criteria) this;
140 | }
141 |
142 | public Criteria andIdLessThanOrEqualTo(Long value) {
143 | addCriterion("id <=", value, "id");
144 | return (Criteria) this;
145 | }
146 |
147 | public Criteria andIdIn(List values) {
148 | addCriterion("id in", values, "id");
149 | return (Criteria) this;
150 | }
151 |
152 | public Criteria andIdNotIn(List values) {
153 | addCriterion("id not in", values, "id");
154 | return (Criteria) this;
155 | }
156 |
157 | public Criteria andIdBetween(Long value1, Long value2) {
158 | addCriterion("id between", value1, value2, "id");
159 | return (Criteria) this;
160 | }
161 |
162 | public Criteria andIdNotBetween(Long value1, Long value2) {
163 | addCriterion("id not between", value1, value2, "id");
164 | return (Criteria) this;
165 | }
166 |
167 | public Criteria andNameIsNull() {
168 | addCriterion("name is null");
169 | return (Criteria) this;
170 | }
171 |
172 | public Criteria andNameIsNotNull() {
173 | addCriterion("name is not null");
174 | return (Criteria) this;
175 | }
176 |
177 | public Criteria andNameEqualTo(String value) {
178 | addCriterion("name =", value, "name");
179 | return (Criteria) this;
180 | }
181 |
182 | public Criteria andNameNotEqualTo(String value) {
183 | addCriterion("name <>", value, "name");
184 | return (Criteria) this;
185 | }
186 |
187 | public Criteria andNameGreaterThan(String value) {
188 | addCriterion("name >", value, "name");
189 | return (Criteria) this;
190 | }
191 |
192 | public Criteria andNameGreaterThanOrEqualTo(String value) {
193 | addCriterion("name >=", value, "name");
194 | return (Criteria) this;
195 | }
196 |
197 | public Criteria andNameLessThan(String value) {
198 | addCriterion("name <", value, "name");
199 | return (Criteria) this;
200 | }
201 |
202 | public Criteria andNameLessThanOrEqualTo(String value) {
203 | addCriterion("name <=", value, "name");
204 | return (Criteria) this;
205 | }
206 |
207 | public Criteria andNameLike(String value) {
208 | addCriterion("name like", value, "name");
209 | return (Criteria) this;
210 | }
211 |
212 | public Criteria andNameNotLike(String value) {
213 | addCriterion("name not like", value, "name");
214 | return (Criteria) this;
215 | }
216 |
217 | public Criteria andNameIn(List values) {
218 | addCriterion("name in", values, "name");
219 | return (Criteria) this;
220 | }
221 |
222 | public Criteria andNameNotIn(List values) {
223 | addCriterion("name not in", values, "name");
224 | return (Criteria) this;
225 | }
226 |
227 | public Criteria andNameBetween(String value1, String value2) {
228 | addCriterion("name between", value1, value2, "name");
229 | return (Criteria) this;
230 | }
231 |
232 | public Criteria andNameNotBetween(String value1, String value2) {
233 | addCriterion("name not between", value1, value2, "name");
234 | return (Criteria) this;
235 | }
236 | }
237 |
238 | public static class Criteria extends GeneratedCriteria {
239 |
240 | protected Criteria() {
241 | super();
242 | }
243 | }
244 |
245 | public static class Criterion {
246 | private String condition;
247 |
248 | private Object value;
249 |
250 | private Object secondValue;
251 |
252 | private boolean noValue;
253 |
254 | private boolean singleValue;
255 |
256 | private boolean betweenValue;
257 |
258 | private boolean listValue;
259 |
260 | private String typeHandler;
261 |
262 | public String getCondition() {
263 | return condition;
264 | }
265 |
266 | public Object getValue() {
267 | return value;
268 | }
269 |
270 | public Object getSecondValue() {
271 | return secondValue;
272 | }
273 |
274 | public boolean isNoValue() {
275 | return noValue;
276 | }
277 |
278 | public boolean isSingleValue() {
279 | return singleValue;
280 | }
281 |
282 | public boolean isBetweenValue() {
283 | return betweenValue;
284 | }
285 |
286 | public boolean isListValue() {
287 | return listValue;
288 | }
289 |
290 | public String getTypeHandler() {
291 | return typeHandler;
292 | }
293 |
294 | protected Criterion(String condition) {
295 | super();
296 | this.condition = condition;
297 | this.typeHandler = null;
298 | this.noValue = true;
299 | }
300 |
301 | protected Criterion(String condition, Object value, String typeHandler) {
302 | super();
303 | this.condition = condition;
304 | this.value = value;
305 | this.typeHandler = typeHandler;
306 | if (value instanceof List>) {
307 | this.listValue = true;
308 | } else {
309 | this.singleValue = true;
310 | }
311 | }
312 |
313 | protected Criterion(String condition, Object value) {
314 | this(condition, value, null);
315 | }
316 |
317 | protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
318 | super();
319 | this.condition = condition;
320 | this.value = value;
321 | this.secondValue = secondValue;
322 | this.typeHandler = typeHandler;
323 | this.betweenValue = true;
324 | }
325 |
326 | protected Criterion(String condition, Object value, Object secondValue) {
327 | this(condition, value, secondValue, null);
328 | }
329 | }
330 | }
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/spring-boot-mybatis-rw-sample/src/main/java/com/github/chenlei2/springboot/mybatis/rw/sample/mapper/StudentsExample.java:
--------------------------------------------------------------------------------
1 | package com.github.chenlei2.springboot.mybatis.rw.sample.mapper;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class StudentsExample {
7 | protected String orderByClause;
8 |
9 | protected boolean distinct;
10 |
11 | protected List oredCriteria;
12 |
13 | public StudentsExample() {
14 | oredCriteria = new ArrayList();
15 | }
16 |
17 | public void setOrderByClause(String orderByClause) {
18 | this.orderByClause = orderByClause;
19 | }
20 |
21 | public String getOrderByClause() {
22 | return orderByClause;
23 | }
24 |
25 | public void setDistinct(boolean distinct) {
26 | this.distinct = distinct;
27 | }
28 |
29 | public boolean isDistinct() {
30 | return distinct;
31 | }
32 |
33 | public List getOredCriteria() {
34 | return oredCriteria;
35 | }
36 |
37 | public void or(Criteria criteria) {
38 | oredCriteria.add(criteria);
39 | }
40 |
41 | public Criteria or() {
42 | Criteria criteria = createCriteriaInternal();
43 | oredCriteria.add(criteria);
44 | return criteria;
45 | }
46 |
47 | public Criteria createCriteria() {
48 | Criteria criteria = createCriteriaInternal();
49 | if (oredCriteria.size() == 0) {
50 | oredCriteria.add(criteria);
51 | }
52 | return criteria;
53 | }
54 |
55 | protected Criteria createCriteriaInternal() {
56 | Criteria criteria = new Criteria();
57 | return criteria;
58 | }
59 |
60 | public void clear() {
61 | oredCriteria.clear();
62 | orderByClause = null;
63 | distinct = false;
64 | }
65 |
66 | protected abstract static class GeneratedCriteria {
67 | protected List criteria;
68 |
69 | protected GeneratedCriteria() {
70 | super();
71 | criteria = new ArrayList();
72 | }
73 |
74 | public boolean isValid() {
75 | return criteria.size() > 0;
76 | }
77 |
78 | public List getAllCriteria() {
79 | return criteria;
80 | }
81 |
82 | public List getCriteria() {
83 | return criteria;
84 | }
85 |
86 | protected void addCriterion(String condition) {
87 | if (condition == null) {
88 | throw new RuntimeException("Value for condition cannot be null");
89 | }
90 | criteria.add(new Criterion(condition));
91 | }
92 |
93 | protected void addCriterion(String condition, Object value, String property) {
94 | if (value == null) {
95 | throw new RuntimeException("Value for " + property + " cannot be null");
96 | }
97 | criteria.add(new Criterion(condition, value));
98 | }
99 |
100 | protected void addCriterion(String condition, Object value1, Object value2, String property) {
101 | if (value1 == null || value2 == null) {
102 | throw new RuntimeException("Between values for " + property + " cannot be null");
103 | }
104 | criteria.add(new Criterion(condition, value1, value2));
105 | }
106 |
107 | public Criteria andIdIsNull() {
108 | addCriterion("id is null");
109 | return (Criteria) this;
110 | }
111 |
112 | public Criteria andIdIsNotNull() {
113 | addCriterion("id is not null");
114 | return (Criteria) this;
115 | }
116 |
117 | public Criteria andIdEqualTo(Long value) {
118 | addCriterion("id =", value, "id");
119 | return (Criteria) this;
120 | }
121 |
122 | public Criteria andIdNotEqualTo(Long value) {
123 | addCriterion("id <>", value, "id");
124 | return (Criteria) this;
125 | }
126 |
127 | public Criteria andIdGreaterThan(Long value) {
128 | addCriterion("id >", value, "id");
129 | return (Criteria) this;
130 | }
131 |
132 | public Criteria andIdGreaterThanOrEqualTo(Long value) {
133 | addCriterion("id >=", value, "id");
134 | return (Criteria) this;
135 | }
136 |
137 | public Criteria andIdLessThan(Long value) {
138 | addCriterion("id <", value, "id");
139 | return (Criteria) this;
140 | }
141 |
142 | public Criteria andIdLessThanOrEqualTo(Long value) {
143 | addCriterion("id <=", value, "id");
144 | return (Criteria) this;
145 | }
146 |
147 | public Criteria andIdIn(List values) {
148 | addCriterion("id in", values, "id");
149 | return (Criteria) this;
150 | }
151 |
152 | public Criteria andIdNotIn(List values) {
153 | addCriterion("id not in", values, "id");
154 | return (Criteria) this;
155 | }
156 |
157 | public Criteria andIdBetween(Long value1, Long value2) {
158 | addCriterion("id between", value1, value2, "id");
159 | return (Criteria) this;
160 | }
161 |
162 | public Criteria andIdNotBetween(Long value1, Long value2) {
163 | addCriterion("id not between", value1, value2, "id");
164 | return (Criteria) this;
165 | }
166 |
167 | public Criteria andNameIsNull() {
168 | addCriterion("name is null");
169 | return (Criteria) this;
170 | }
171 |
172 | public Criteria andNameIsNotNull() {
173 | addCriterion("name is not null");
174 | return (Criteria) this;
175 | }
176 |
177 | public Criteria andNameEqualTo(String value) {
178 | addCriterion("name =", value, "name");
179 | return (Criteria) this;
180 | }
181 |
182 | public Criteria andNameNotEqualTo(String value) {
183 | addCriterion("name <>", value, "name");
184 | return (Criteria) this;
185 | }
186 |
187 | public Criteria andNameGreaterThan(String value) {
188 | addCriterion("name >", value, "name");
189 | return (Criteria) this;
190 | }
191 |
192 | public Criteria andNameGreaterThanOrEqualTo(String value) {
193 | addCriterion("name >=", value, "name");
194 | return (Criteria) this;
195 | }
196 |
197 | public Criteria andNameLessThan(String value) {
198 | addCriterion("name <", value, "name");
199 | return (Criteria) this;
200 | }
201 |
202 | public Criteria andNameLessThanOrEqualTo(String value) {
203 | addCriterion("name <=", value, "name");
204 | return (Criteria) this;
205 | }
206 |
207 | public Criteria andNameLike(String value) {
208 | addCriterion("name like", value, "name");
209 | return (Criteria) this;
210 | }
211 |
212 | public Criteria andNameNotLike(String value) {
213 | addCriterion("name not like", value, "name");
214 | return (Criteria) this;
215 | }
216 |
217 | public Criteria andNameIn(List values) {
218 | addCriterion("name in", values, "name");
219 | return (Criteria) this;
220 | }
221 |
222 | public Criteria andNameNotIn(List values) {
223 | addCriterion("name not in", values, "name");
224 | return (Criteria) this;
225 | }
226 |
227 | public Criteria andNameBetween(String value1, String value2) {
228 | addCriterion("name between", value1, value2, "name");
229 | return (Criteria) this;
230 | }
231 |
232 | public Criteria andNameNotBetween(String value1, String value2) {
233 | addCriterion("name not between", value1, value2, "name");
234 | return (Criteria) this;
235 | }
236 | }
237 |
238 | public static class Criteria extends GeneratedCriteria {
239 |
240 | protected Criteria() {
241 | super();
242 | }
243 | }
244 |
245 | public static class Criterion {
246 | private String condition;
247 |
248 | private Object value;
249 |
250 | private Object secondValue;
251 |
252 | private boolean noValue;
253 |
254 | private boolean singleValue;
255 |
256 | private boolean betweenValue;
257 |
258 | private boolean listValue;
259 |
260 | private String typeHandler;
261 |
262 | public String getCondition() {
263 | return condition;
264 | }
265 |
266 | public Object getValue() {
267 | return value;
268 | }
269 |
270 | public Object getSecondValue() {
271 | return secondValue;
272 | }
273 |
274 | public boolean isNoValue() {
275 | return noValue;
276 | }
277 |
278 | public boolean isSingleValue() {
279 | return singleValue;
280 | }
281 |
282 | public boolean isBetweenValue() {
283 | return betweenValue;
284 | }
285 |
286 | public boolean isListValue() {
287 | return listValue;
288 | }
289 |
290 | public String getTypeHandler() {
291 | return typeHandler;
292 | }
293 |
294 | protected Criterion(String condition) {
295 | super();
296 | this.condition = condition;
297 | this.typeHandler = null;
298 | this.noValue = true;
299 | }
300 |
301 | protected Criterion(String condition, Object value, String typeHandler) {
302 | super();
303 | this.condition = condition;
304 | this.value = value;
305 | this.typeHandler = typeHandler;
306 | if (value instanceof List>) {
307 | this.listValue = true;
308 | } else {
309 | this.singleValue = true;
310 | }
311 | }
312 |
313 | protected Criterion(String condition, Object value) {
314 | this(condition, value, null);
315 | }
316 |
317 | protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
318 | super();
319 | this.condition = condition;
320 | this.value = value;
321 | this.secondValue = secondValue;
322 | this.typeHandler = typeHandler;
323 | this.betweenValue = true;
324 | }
325 |
326 | protected Criterion(String condition, Object value, Object secondValue) {
327 | this(condition, value, secondValue, null);
328 | }
329 | }
330 | }
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/spring-boot-mybatis-rw-starter/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
22 |
46 |
49 |
55 | D:\repo
56 |
64 |
65 |
72 |
73 |
78 |
79 |
83 |
84 |
85 |
90 |
91 |
105 |
106 |
107 |
111 |
112 |
125 |
126 |
133 |
134 | oss
135 | chenlei
136 | C?l1335198mvn
137 |
138 |
139 |
140 |
151 |
152 |
164 |
165 |
166 |
187 |
188 |
217 |
218 |
252 |
253 | oss
254 |
255 | true
256 |
257 |
258 | gpg
259 | C?l135198
260 |
261 |
262 |
263 |
264 |
265 |
273 |
274 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/spring-boot-mybatis-rw-starter/src/main/java/com/github/chenlei2/springboot/mybatis/rw/starter/datasource/DataSourceProxy.java:
--------------------------------------------------------------------------------
1 | package com.github.chenlei2.springboot.mybatis.rw.starter.datasource;
2 |
3 | import java.io.PrintWriter;
4 | import java.lang.reflect.InvocationHandler;
5 | import java.lang.reflect.InvocationTargetException;
6 | import java.lang.reflect.Method;
7 | import java.lang.reflect.Proxy;
8 | import java.sql.Connection;
9 | import java.sql.SQLException;
10 | import java.util.Map;
11 | import java.util.logging.Logger;
12 |
13 | import javax.sql.DataSource;
14 |
15 | import org.apache.commons.logging.Log;
16 | import org.apache.commons.logging.LogFactory;
17 | import org.springframework.core.Constants;
18 | import org.springframework.jdbc.datasource.ConnectionProxy;
19 |
20 | /**
21 | * 数据库代理,具体数据源由DataSourceRout提供
22 | *
23 | * @author chenlei
24 | *
25 | */
26 | public class DataSourceProxy implements DataSource {
27 |
28 | /** Constants instance for TransactionDefinition */
29 | private static final Constants constants = new Constants(Connection.class);
30 |
31 | private static final Log logger = LogFactory.getLog(DataSourceProxy.class);
32 |
33 | private Boolean defaultAutoCommit = Boolean.TRUE;
34 |
35 | private Integer defaultTransactionIsolation = 2;
36 |
37 | private DataSourceRout dataSourceRout;
38 |
39 | /**
40 | * Create a new LazyConnectionDataSourceProxy.a
41 | *
42 | * @see #setTargetDataSource
43 | */
44 | public DataSourceProxy(DataSourceRout dataSourceRout) {
45 | this.dataSourceRout = dataSourceRout;
46 | }
47 |
48 | /**
49 | * Set the default auto-commit mode to expose when no target Connection has
50 | * been fetched yet (-> actual JDBC Connection default not known yet).
51 | *
52 | * If not specified, the default gets determined by checking a target
53 | * Connection on startup. If that check fails, the default will be
54 | * determined lazily on first access of a Connection.
55 | *
56 | * @see java.sql.Connection#setAutoCommit
57 | */
58 | public void setDefaultAutoCommit(boolean defaultAutoCommit) {
59 | this.defaultAutoCommit = defaultAutoCommit;
60 | }
61 |
62 | /**
63 | * Set the default transaction isolation level to expose when no target
64 | * Connection has been fetched yet (-> actual JDBC Connection default not
65 | * known yet).
66 | *
67 | * This property accepts the int constant value (e.g. 8) as defined in the
68 | * {@link java.sql.Connection} interface; it is mainly intended for
69 | * programmatic use. Consider using the "defaultTransactionIsolationName"
70 | * property for setting the value by name (e.g. "TRANSACTION_SERIALIZABLE").
71 | *
72 | * If not specified, the default gets determined by checking a target
73 | * Connection on startup. If that check fails, the default will be
74 | * determined lazily on first access of a Connection.
75 | *
76 | * @see #setDefaultTransactionIsolationName
77 | * @see java.sql.Connection#setTransactionIsolation
78 | */
79 | public void setDefaultTransactionIsolation(int defaultTransactionIsolation) {
80 | this.defaultTransactionIsolation = defaultTransactionIsolation;
81 | }
82 |
83 | /**
84 | * Set the default transaction isolation level by the name of the
85 | * corresponding constant in {@link java.sql.Connection}, e.g.
86 | * "TRANSACTION_SERIALIZABLE".
87 | *
88 | * @param constantName
89 | * name of the constant
90 | * @see #setDefaultTransactionIsolation
91 | * @see java.sql.Connection#TRANSACTION_READ_UNCOMMITTED
92 | * @see java.sql.Connection#TRANSACTION_READ_COMMITTED
93 | * @see java.sql.Connection#TRANSACTION_REPEATABLE_READ
94 | * @see java.sql.Connection#TRANSACTION_SERIALIZABLE
95 | */
96 | public void setDefaultTransactionIsolationName(String constantName) {
97 | setDefaultTransactionIsolation(constants.asNumber(constantName).intValue());
98 | }
99 |
100 | /**
101 | * Expose the default auto-commit value.
102 | */
103 | protected Boolean defaultAutoCommit() {
104 | return this.defaultAutoCommit;
105 | }
106 |
107 | /**
108 | * Expose the default transaction isolation value.
109 | */
110 | protected Integer defaultTransactionIsolation() {
111 | return this.defaultTransactionIsolation;
112 | }
113 |
114 | /**
115 | * Return a Connection handle that lazily fetches an actual JDBC Connection
116 | * when asked for a Statement (or PreparedStatement or CallableStatement).
117 | *
118 | * The returned Connection handle implements the ConnectionProxy interface,
119 | * allowing to retrieve the underlying target Connection.
120 | *
121 | * @return a lazy Connection handle
122 | * @see ConnectionProxy#getTargetConnection()
123 | */
124 | @Override
125 | public Connection getConnection() throws SQLException {
126 | return (Connection) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(),
127 | new Class>[] { ConnectionProxy.class }, new LazyConnectionInvocationHandler());
128 | }
129 |
130 | /**
131 | * Return a Connection handle that lazily fetches an actual JDBC Connection
132 | * when asked for a Statement (or PreparedStatement or CallableStatement).
133 | *
134 | * The returned Connection handle implements the ConnectionProxy interface,
135 | * allowing to retrieve the underlying target Connection.
136 | *
137 | * @param username
138 | * the per-Connection username
139 | * @param password
140 | * the per-Connection password
141 | * @return a lazy Connection handle
142 | * @see ConnectionProxy#getTargetConnection()
143 | */
144 | @Override
145 | public Connection getConnection(String username, String password) throws SQLException {
146 | return (Connection) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(),
147 | new Class>[] { ConnectionProxy.class }, new LazyConnectionInvocationHandler(username, password));
148 | }
149 |
150 | /**
151 | * Invocation handler that defers fetching an actual JDBC Connection until
152 | * first creation of a Statement.
153 | */
154 | private class LazyConnectionInvocationHandler implements InvocationHandler {
155 |
156 | private String username;
157 |
158 | private String password;
159 |
160 | private Boolean readOnly = Boolean.FALSE;
161 |
162 | private Integer transactionIsolation;
163 |
164 | private Boolean autoCommit;
165 |
166 | private boolean closed = false;
167 |
168 | public LazyConnectionInvocationHandler() {
169 | this.autoCommit = defaultAutoCommit();
170 | this.transactionIsolation = defaultTransactionIsolation();
171 | }
172 |
173 | public LazyConnectionInvocationHandler(String username, String password) {
174 | this();
175 | this.username = username;
176 | this.password = password;
177 | }
178 |
179 | @Override
180 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
181 | // Invocation on ConnectionProxy interface coming in...
182 | if (method.getName().equals("setTransactionIsolation") && args != null && (Integer) args[0] == Connection.TRANSACTION_SERIALIZABLE) {
183 | args[0] = defaultTransactionIsolation();
184 | ConnectionHold.FORCE_WRITE.set(Boolean.TRUE);
185 | }
186 | if (method.getName().equals("equals")) {
187 | // We must avoid fetching a target Connection for "equals".
188 | // Only consider equal when proxies are identical.
189 | return (proxy == args[0]);
190 | } else if (method.getName().equals("hashCode")) {
191 | // We must avoid fetching a target Connection for "hashCode",
192 | // and we must return the same hash code even when the target
193 | // Connection has been fetched: use hashCode of Connection
194 | // proxy.
195 | return System.identityHashCode(proxy);
196 | } else if (method.getName().equals("unwrap")) {
197 | if (((Class>) args[0]).isInstance(proxy)) {
198 | return proxy;
199 | }
200 | } else if (method.getName().equals("isWrapperFor")) {
201 | if (((Class>) args[0]).isInstance(proxy)) {
202 | return true;
203 | }
204 | } else if (method.getName().equals("getTargetConnection")) {
205 | // Handle getTargetConnection method: return underlying
206 | // connection.
207 | return getTargetConnection(method);
208 | }
209 |
210 | if (!hasTargetConnection()) {
211 | // No physical target Connection kept yet ->
212 | // resolve transaction demarcation methods without fetching
213 | // a physical JDBC Connection until absolutely necessary.
214 |
215 | if (method.getName().equals("toString")) {
216 | return "Lazy Connection proxy for target DataSource [" + dataSourceRout.getTargetDataSource() + "]";
217 | } else if (method.getName().equals("isReadOnly")) {
218 | return this.readOnly;
219 | } else if (method.getName().equals("setReadOnly")) {
220 | this.readOnly = (Boolean) args[0];
221 | return null;
222 | } else if (method.getName().equals("getTransactionIsolation")) {
223 | if (this.transactionIsolation != null) {
224 | return this.transactionIsolation;
225 | }
226 | // Else fetch actual Connection and check there,
227 | // because we didn't have a default specified.
228 | } else if (method.getName().equals("setTransactionIsolation")) {
229 | this.transactionIsolation = (Integer) args[0];
230 | return null;
231 | } else if (method.getName().equals("getAutoCommit")) {
232 | if (this.autoCommit != null) {
233 | return this.autoCommit;
234 | }
235 | // Else fetch actual Connection and check there,
236 | // because we didn't have a default specified.
237 | } else if (method.getName().equals("setAutoCommit")) {
238 | this.autoCommit = (Boolean) args[0];
239 | return null;
240 | } else if (method.getName().equals("commit")) {
241 | // Ignore: no statements created yet.
242 | return null;
243 | } else if (method.getName().equals("rollback")) {
244 | // Ignore: no statements created yet.
245 | return null;
246 | } else if (method.getName().equals("getWarnings")) {
247 | return null;
248 | } else if (method.getName().equals("clearWarnings")) {
249 | return null;
250 | } else if (method.getName().equals("close")) {
251 | // Ignore: no target connection yet.
252 | this.closed = true;
253 | return null;
254 | } else if (method.getName().equals("isClosed")) {
255 | return this.closed;
256 | } else if (this.closed) {
257 | // Connection proxy closed, without ever having fetched a
258 | // physical JDBC Connection: throw corresponding
259 | // SQLException.
260 | throw new SQLException("Illegal operation: connection is closed");
261 | }
262 | } else {
263 | if (method.getName().equals("commit")) {
264 | Map connectionMap = ConnectionHold.CONNECTION_CONTEXT.get();
265 | Connection writeCon = connectionMap.get(ConnectionHold.WRITE);
266 | if (writeCon != null) {
267 | writeCon.commit();
268 | }
269 | return null;
270 | }
271 | if (method.getName().equals("rollback")) {
272 | Map connectionMap = ConnectionHold.CONNECTION_CONTEXT.get();
273 | Connection writeCon = connectionMap.get(ConnectionHold.WRITE);
274 | if (writeCon != null) {
275 | writeCon.rollback();
276 | }
277 | return null;
278 | }
279 | if (method.getName().equals("close")) {
280 | ConnectionHold.FORCE_WRITE.set(Boolean.FALSE);
281 | Map connectionMap = ConnectionHold.CONNECTION_CONTEXT.get();
282 | Connection readCon = connectionMap.remove(ConnectionHold.READ);
283 | if (readCon != null) {
284 | readCon.close();
285 | }
286 | Connection writeCon = connectionMap.remove(ConnectionHold.WRITE);
287 | if (writeCon != null) {
288 | writeCon.close();
289 | }
290 | this.closed = true;
291 | return null;
292 | }
293 | }
294 |
295 | // Target Connection already fetched,
296 | // or target Connection necessary for current operation ->
297 | // invoke method on target connection.
298 |
299 | try {
300 | return method.invoke(
301 | ConnectionHold.CONNECTION_CONTEXT.get().get(ConnectionHold.CURRENT_CONNECTION.get()), args);
302 | } catch (InvocationTargetException ex) {
303 | throw ex.getTargetException();
304 | }
305 | }
306 |
307 | /**
308 | * Return whether the proxy currently holds a target Connection.
309 | */
310 | private boolean hasTargetConnection() {
311 | return (ConnectionHold.CONNECTION_CONTEXT.get() != null
312 | && ConnectionHold.CONNECTION_CONTEXT.get().get(ConnectionHold.CURRENT_CONNECTION.get()) != null);
313 | }
314 |
315 | /**
316 | * Return the target Connection, fetching it and initializing it if
317 | * necessary.
318 | */
319 | private Connection getTargetConnection(Method operation) throws SQLException {
320 | // No target Connection held -> fetch one.
321 | if (logger.isDebugEnabled()) {
322 | logger.debug("Connecting to database for operation '" + operation.getName() + "'");
323 | }
324 |
325 | // Fetch physical Connection from DataSource.
326 | Connection target = (this.username != null)
327 | ? dataSourceRout.getTargetDataSource().getConnection(this.username, this.password)
328 | : dataSourceRout.getTargetDataSource().getConnection();
329 |
330 | // Apply kept transaction settings, if any.
331 | if (this.readOnly) {
332 | try {
333 | target.setReadOnly(this.readOnly);
334 | } catch (Exception ex) {
335 | // "read-only not supported" -> ignore, it's just a hint
336 | // anyway
337 | logger.debug("Could not set JDBC Connection read-only", ex);
338 | }
339 | }
340 | if (this.transactionIsolation != null && !this.transactionIsolation.equals(defaultTransactionIsolation())) {
341 | target.setTransactionIsolation(this.transactionIsolation);
342 | }
343 | if (ConnectionHold.READ.equals(ConnectionHold.CURRENT_CONNECTION.get())) {
344 | try {
345 | target.setAutoCommit(true);
346 | } catch (SQLException e) {
347 | e.printStackTrace();
348 | }
349 | }
350 |
351 | if (this.autoCommit != null && this.autoCommit != target.getAutoCommit()) {
352 | if (ConnectionHold.WRITE.equals(ConnectionHold.CURRENT_CONNECTION.get())) {
353 | target.setAutoCommit(this.autoCommit);
354 | }
355 | }
356 | return target;
357 | }
358 | }
359 |
360 | @Override
361 | public PrintWriter getLogWriter() throws SQLException {
362 | return dataSourceRout.getTargetDataSource().getLogWriter();
363 | }
364 |
365 | @Override
366 | public void setLogWriter(PrintWriter out) throws SQLException {
367 | dataSourceRout.getTargetDataSource().setLogWriter(out);
368 | }
369 |
370 | @Override
371 | public int getLoginTimeout() throws SQLException {
372 | return dataSourceRout.getTargetDataSource().getLoginTimeout();
373 | }
374 |
375 | @Override
376 | public void setLoginTimeout(int seconds) throws SQLException {
377 | dataSourceRout.getTargetDataSource().setLoginTimeout(seconds);
378 | }
379 |
380 | // ---------------------------------------------------------------------
381 | // Implementation of JDBC 4.0's Wrapper interface
382 | // ---------------------------------------------------------------------
383 |
384 | @Override
385 | @SuppressWarnings("unchecked")
386 | public T unwrap(Class iface) throws SQLException {
387 | if (iface.isInstance(this)) {
388 | return (T) this;
389 | }
390 | return dataSourceRout.getTargetDataSource().unwrap(iface);
391 | }
392 |
393 | @Override
394 | public boolean isWrapperFor(Class> iface) throws SQLException {
395 | return (iface.isInstance(this) || dataSourceRout.getTargetDataSource().isWrapperFor(iface));
396 | }
397 |
398 | // ---------------------------------------------------------------------
399 | // Implementation of JDBC 4.1's getParentLogger method
400 | // ---------------------------------------------------------------------
401 |
402 | @Override
403 | public Logger getParentLogger() {
404 | return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
405 | }
406 | }
407 |
--------------------------------------------------------------------------------
/spring-boot-mybatis-rw/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
81 |
82 |
83 |
84 |
85 | true
86 | DEFINITION_ORDER
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 | 1569204962384
192 |
193 |
194 | 1569204962384
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 | 1.8
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 | 1.8
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
--------------------------------------------------------------------------------