目录
这个系列的其他文章:
CrateDB初探(一):CrateDB集群的Docker部署
CrateDB初探(二):PARTITION, SHARDING AND REPLICATION
CrateDB初探(四):乐观并发控制 (Optimistic Concurrency Control )
本文简略地介绍了通过JDBC连接到CrateDB,并进行简单数据操作。
过程大致与连接到其他数据库(比如mysql)相似,需要注意驱动与db版本兼容性问题
兼容性
jdbc驱动与CrateDB版本需要遵循以下兼容性
注意:
- 当CrateDB版本在2.1.x以后,通过jdbc连接时必须提供一个用户名,默认是crate
- 在实现细节方面,数据源无法使用(DataSource is not supported.)这里有疑问,见Spring+CrateDB小节
添加Maven依赖
- 添加bintray仓库
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>cratedbmvn</id>
<name>bintray</name>
<url>http://dl.bintray.com/crate/crate</url>
</repository>
- pom.xml中添加crate-jdbc依赖(注册CrateDB JDBC驱动)
<!-- https://mvnrepository.com/artifact/io.crate/crate-jdbc -->
<dependency>
<groupId>io.crate</groupId>
<artifactId>crate-jdbc</artifactId>
<version>2.3.1</version>
</dependency>
使用Java JDBC原生方式
准备工作
- 通过docker启动一个实例
sudo docker run --name=crate-test1 -p 4211:4200 -p 5432:5432 --rm -d crate
注:5432是jdbc连接的默认端口,详见参数psql.port
- 新增一个测试表
代码实现
采用properties方式建立jdbc链接,上文的兼容性说明中已经提到高版本db需要设置user参数(默认crate)
Properties properties = new Properties();
properties.put("user", "crate");
Connection conn = DriverManager.getConnection(
"crate://localhost:5432/",
properties);
或者使用CrateDriver(对connection URL的有效性进行检验)
Properties properties = new Properties();
properties.put("user", "crate");
// io.crate.client.jdbc.CrateDriver
CrateDriver driver = new CrateDriver();
Connection conn = driver.connect("crate://localhost:5432/", properties);
默认schema为doc,如果要切换:conn.setSchema("my_schema");
参数:
- user:DB版本2.1.x以上必须设置,如果没有自定义设置过数据库用户,则为CrateDB superuser, 即crate
- strict:当设置为false,如果用到了CrateDB不支持的特性(比如事务)程序不会抛出异常,默默执行; 当设置为true,则会抛出异常。默认为false。
- 其他参数见官方文档
数据操作
因为官方说无法使用datasource(实现细节),因此考虑用最原始的方式进行数据操作(这里有个疑问,实际是可以使用datasource的,见下面Spring+CrateDB小节)
插入
PreparedStatement ps2 = conn.prepareStatement("INSERT INTO test_tab VALUES(2,'cj')");
ps2.execute();
查询
PreparedStatement ps = conn.prepareStatement("select name from test_tab where id=1");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
String name = rs.getString("name");
System.out.println("rs: " + name);
}
关于FetchSize的设置
设置fetch size可以提高查询读取速度,在CrateDB中正确使用fetch size的姿势是需要将autoCommit设置为false
* We are allowed to disable autoCommit (= manual commit) if strict mode is not enabled.
* Manual commit is required for {@link org.postgresql.jdbc.PgStatement#setFetchSize(int)} to work correctly!
* If autoCommit is true {@link org.postgresql.jdbc.PgStatement#executeInternal(CachedQuery, ParameterList, int)}
* will never set the QueryExecutor.QUERY_FORWARD_CURSOR flag and therefore fetch all results at once instead of
* batching them.
官方示例代码
在查询前statement.setFetchSize(10);
/**
* fetch size and execution flag is correctly appied if autoCommit == false
*/
@Test
public void testFetchSizeNotIgnoredIfManualCommit() throws Exception {
try (Connection connection = DriverManager.getConnection(getConnectionString())) {
connection.setAutoCommit(false);
try (Statement statement = connection.createStatement()) {
statement.setFetchSize(10);
statement.execute("select * from sys.summits");
ResultSet rs = statement.getResultSet();
assertEquals(10, rs.getFetchSize());
Field rowsField = rs.getClass().getDeclaredField("rows");
rowsField.setAccessible(true);
List rows = (List) rowsField.get(rs);
assertEquals(10, rows.size());
}
}
}
代码片段
package nb.test.cratedb;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
* Hello world!
*
*/
public class App {
public static void main(String[] args) throws InterruptedException, SQLException {
Connection conn = null;
try {
Properties properties = new Properties();
properties.put("user", "crate");
conn = DriverManager.getConnection("crate://localhost:5432/", properties);
System.out.println("Connection: " + conn);
PreparedStatement ps2 = conn.prepareStatement("INSERT INTO test_tab VALUES(2,'cj')");
ps2.execute();
PreparedStatement ps = conn.prepareStatement("select id, name from test_tab");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println("id: " + id + ", name: " + name);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
conn.close();
}
Thread.sleep(10000);
}
}
输出
官方的crate-jdbc使用范例
Spring+CrateDB方式
使用了原始的Spring-jdbc方式连接到CrateDB
配置文件
增加数据源依赖jar包
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.6.0</version>
</dependency>
db.properties
配置驱动类名、url、用户名(密码为空)
jdbc.driverClassName=io.crate.client.jdbc.CrateDriver
jdbc.url=crate://localhost:5432/
jdbc.username=crate
jdbc.password=
application.xml
配置数据源、JdbcTemplate的java bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<context:property-placeholder location="classpath:db.properties"/>
<bean id="datasource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="maxTotal" value="50" />
<property name="maxWaitMillis" value="6000" />
<property name="initialSize" value="10" />
<property name="maxIdle" value="20"></property>
<property name="minIdle" value="10" />
</bean>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="datasource" />
</bean>
</beans>
代码片段
public static void main(String[] args) throws SQLException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
DataSource dbsource = (DataSource) ctx.getBean("datasource");
NamedParameterJdbcTemplate namedTemplate = (NamedParameterJdbcTemplate) ctx.getBean("namedParameterJdbcTemplate");
String sql = "SELECT id, name FROM doc.tmp1";
Map<String, Object> paramsMap = new HashMap<String, Object>();
SqlRowSet rowSet = namedTemplate.queryForRowSet(sql, paramsMap);
while(rowSet.next()) {
System.out.println("id: " + rowSet.getInt("id") + " , name: " + rowSet.getString("name"));
}
}
DataSource的问题
在CrateDB官方文档中提到
DataSource is not supported.
我的理解是还是可以引入第三方的数据源jar包来支持,比如本例中使用的dbcp2
为验证数据源是否真正起了作用,做个简陋的实验:
上例中conn2被关闭,但不影响conn查询数据,说明conn2和conn确实是两个从数据源中获得的不同连接。
Spring+MyBatis+CrateDB方式
引入MyBatis作为ORM框架,在上节基础上测试连接
配置文件
增加mybatis的jar包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING" />
<setting name="defaultStatementTimeout" value="25" />
</settings>
<typeAliases>
<typeAlias type="nb.test.cratedb.User" alias="User" />
</typeAliases>
</configuration>
增加mybatis的spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource" />
<property name="mapperLocations">
<list>
<value>classpath:nb/test/orm/*.xml</value>
</list>
</property>
<property name="configLocation"
value="classpath:mybatis-config.xml" />
</bean>
<bean id="mapperScannerConfigurerMotan" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="nb.test.orm" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
</beans>
代码片段
实体类
public class User implements Serializable {
private static final long serialVersionUID = 2686795246219114561L;
private int id;
private String name;
/* 忽略getter, setter等代码 */
}
Dao接口
@MapperScan
public interface UserDao {
public User queryUserById(int id);
}
Mapper
<?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="nb.test.orm.UserDao">
<select id="queryUserById" parameterType="int" resultType="User">
SELECT id, name FROM doc.tmp1 t
WHERE t.id = #{id}
</select>
</mapper>
Service实现,简单到可以忽略不计
@Service
public class UserService {
@Autowired
private UserDao userDao;
public User getUser(int id) {
return userDao.queryUserById(id);
}
}
测试代码
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"application.xml", "application-orm.xml"});
UserService userService = (UserService) ctx.getBean("userService");
User user = userService.getUser(1);
System.out.println("User: " + user);
}