JDBC 和连接池
1 JDBC概述
Java DataBase Connectivity,Java数据库连接,一种执行SQL的Java API,为多种关系数据库提供统一访问规范,由一组Java语言编写的类和接口组成。
数据库驱动:各个数据库生产商提供的JDBC实现类。使用统一的JDBC规范,不用专门去了解各个数据库的驱动API。
JDBC 可做三件事:与数据库建立连接、发送SQL、处理结果。
2 JDBC常用方法及增删改查
2.1 JDBC的开发步骤
先导入对应数据库的jar包 mysql-connector-java-5.0.8-bin.jar
创建lib目录,用于存放当前项目需要的所有jar包
选择jar包,右键执行build path / Add to Build Path
- 加载驱动
- 获得连接
- 获得语句执行平台statement
- 执行sql
- 处理结果
- 释放资源
- import org.junit.Test;
- public class JDBCDemo1 {
- @Test
- public void demo1() throws Exception{
- // 1.加载驱动
- Class.forName("com.mysql.jdbc.Driver");
- // 2.获得连接
- Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web_test3", "root", "abc"); //url如果连接的是本机的路径,可以简化为jdbc:mysql:///web_test3 (3个///)
- // 3.基本操作:执行SQL
- // 3.1获得执行SQL语句的对象
- Statement statement = conn.createStatement();
- // 3.2编写SQL语句:
- String sql = "select * from user";
- // 3.3执行SQL:
- ResultSet rs = statement.executeQuery(sql);
- // 3.4遍历结果集:
- while(rs.next()){
- System.out.print(rs.getInt("id")+" ");
- System.out.print(rs.getString("username")+" ");
- System.out.print(rs.getString("password")+" ");
- System.out.print(rs.getString("nickname")+" ");
- System.out.print(rs.getInt("age"));
- System.out.println();
- }
- // 4.释放资源
- rs.close();
- statement.close();
- conn.close();
- }
- }
2 JDBC常用类/接口:
DriverManager类:注册JDBC驱动,获取Connection对象(数据库链接对象,每个Connection代表一个物理连接会话)。
Class.forName("com.mysql.jdbc.Driver");
Connection接口:获取执行SQL的Statement对象,并包含多个用于控制事务的方法。
- Statement createStatement() throws SQLException; //返回一个Statement对象,用来将SQL语句发送到数据库;
- PreparedStatement prepareStatement(String sql)throws SQLException; //返回预编译的Statement对象,即将SQL语句提交到数据库进行预编译;
- CallableStatement prepareCall(String sql) throws SQLException; //返回CallableStatement对象,用于调用存储过程。
- //以上都返回用于执行sql语句的Statement对象,PreparedStatement和CallableStatement是Statement的子类,只有获得了Statement之后才可以执行sql语句;
- void setAutoCommit(boolean autoCommit) throws SQLException; //关闭自动提交,打开事务;
- void rollback() throws SQLException; //回滚事务,并释放Connection对象当前持有的数据库锁;
- void commit() throws SQLException; //提交事务,并释放Connection对象当前持有的数据库锁;
- void setTransactionIsolation(int level) throws SQLException; //设置事务的隔离级别;
- Savepoint setSavepoint() throws SQLException; //创建一个保存点;
- Savepoint setSavepoint(String name) throws SQLException; //以指定名字来创建一个保存点;
- void rollback(Savepoint savepoint) throws SQLException; //将事务回滚到指定的保存点;
Statement接口:用于执行静态SQL语句并返回它所生成结果的对象。
- ResultSet executeQuery(String sql) throws SQLException;//执行查询语句,返回查询结果对应ResultSet对象。该方法只能用于执行查询语句。
- int executeUpdate(String sql) throws SQLException;//执行inset/update/delete语句,返回受影响的行数;也可以执行create/alter/drop/truncate语句,并返回0;
- boolean execute(String sql) throws SQLException;//执行任意sql语句。若执行后第一个结果为ResultSet对象,则返回true;
- //若执行后第一个结果为受影响的行数或无结果,则返回false;
- void addBatch(String sql);//将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中。
- void clearBatch();//清空此Statement对象当前的SQL命令列表。
- int[] executeBatch();//将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。
注意:如果 addBatch() -> executeBatch() 很慢,需要启动批处理操作rewriteBatchedStatements=true,即在数据库连接URL后面加上这个参数:String dbUrl = "jdbc:mysql://localhost:3306/User? rewriteBatchedStatements=true";该参数将多条sql分成若干个报文发送到mysql服务器,并在最后加上commit操作。
ResultSet接口:结果集对象,包含查询结果,可以通过列索引或列名获得列数据。
- boolean next();//将光标从当前位置向前移动一行
- int getInt(int columnIndex);//获取结果集当前行指定列
- int getInt(String columnLabel);//获取结果集当前行指定列
- long getLong(int columnIndex);//获取结果集当前行指定列
- long getLong(String columnLabel);//获取结果集当前行指定列
- String getString(int columnIndex);//获取结果集当前行指定列
- String getString(String columnLabel);//获取结果集当前行指定列
- //示例代码:
- while(resultSet.next()){
- System.out.println(resultSet.getInt("id"))
- System.out.println(resultSet.getString("ename"))
- System.out.println(resultSet.getString("nickname"))
- }
3 JDBC的资源释放
JDBC程序执行结束后,需要将与数据库进行交互的对象释放掉,通常是ResultSet,Statement,Connection。尤其是Connection对象,一定要做到晚创建,早释放。
如果把所有关闭语句写在同一个try块里面,一旦前面的关闭语句抛异常,后面的关闭语句就无法执行,所以要给每个关闭语句一个try块。
最后为了保证资源能够释放,还有在每个关闭语句的后面加一个finally,在finally里给要关闭的资源赋值为空。这样即使关闭过程中抛异常不能及时关闭,但是由于赋值为空,没有引用该资源,在垃圾回收的时候也能够回收。
- if(rs !=null){ try {
- rs.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }finally{
- rs = null;
- }
- }
- if(statement !=null){
- try {
- statement.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }finally{
- statement = null;
- }
- }
- if(conn !=null){
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }finally{
- conn = null;
- }
- }
2.2 JDBC的增删改查
- public void demo1(){
- Connection conn = null;
- Statement stmt = null;
- try{
- Class.forName("com.mysql.jdbc.Driver");
- conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
- stmt = conn.createStatement();
- //增加一条数据
- String sql = "insert into user values (null,'eee','123','阿黄',21)";
- int num = stmt.executeUpdate(sql);
- if(num > 0){
- System.out.println("保存用户成功!!!");
- }
- /*
- //删除、修改类似
- //查询一条或多条多条
- String sql = "select * from user";
- rs = stmt.executeQuery(sql);
- while(rs.next()){
- System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
- }
- */
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- // 资源释放:略
- }
- }
3 JDBC的工具类的抽取
- public class JDBCUtils {
- private static final String driverClassName;
- private static final String url;
- private static final String username;
- private static final String password;
- // 获取属性文件中的内容:
- static{
- driverClassName="com.mysql.jdbc.Driver";
- url="jdbc:mysql:///web_test3";
- username="root";
- password="abc";
- }
- /*
- static{
- // 获取属性文件中的内容:
- Properties properties = new Properties();
- try {
- properties.load(new FileInputStream("src/db.properties"));
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- driverClassName=properties.getProperty("driverClassName");
- url=properties.getProperty("url");
- username=properties.getProperty("username");
- password=properties.getProperty("password");
- }
- */
- /**
- * 注册驱动的方法
- */
- public static void loadDriver(){
- try {
- Class.forName(driverClassName);
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- /**
- * 获得连接的方法
- */
- public static Connection getConnection(){
- Connection conn = null;
- try{
- // 将驱动一并注册:
- loadDriver();
- // 获得连接
- conn = DriverManager.getConnection(url,username, password);
- }catch(Exception e){
- e.printStackTrace();
- }
- return conn;
- }
- /**
- * 释放资源的方法
- */
- public static void release(Statement stmt,Connection conn){
- if(stmt != null){
- try {
- stmt.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- stmt = null;
- }
- if(conn != null){
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- conn = null;
- }
- }
- public static void release(ResultSet rs,Statement stmt,Connection conn){
- // 资源释放:
- if(rs != null){
- try {
- rs.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- rs = null;
- }
- if(stmt != null){
- try {
- stmt.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- stmt = null;
- }
- if(conn != null){
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- conn = null;
- }
- }
- }
测试工具类:
- public void demo1(){
- Connection conn = null;
- Statement stmt = null;
- ResultSet rs = null;
- try{
- conn = JDBCUtils.getConnection();
- stmt = conn.createStatement();
- String sql = "select * from user";
- rs = stmt.executeQuery(sql);
- while(rs.next()){
- System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- JDBCUtils.release(rs, stmt, conn);
- }
- }
4 用 properties 文件配置JDBC信息
1 开发中获得连接的4个参数(驱动、URL、用户名、密码)通常都存在配置文件中,方便后期维护,程序如果需要更换数据库,只需要修改配置文件即可。
使用properties文件要求:
1.文件位置:任意,建议src下
2.文件内容:一行一组数据,格式是“key=value”
a)key命名自定义,如果是多个单词,习惯使用点分隔。例如:jdbc.driver
b)value值不支持中文,如果需要使用非英文字符,将进行unicode转换
- driver=com.mysql.jdbc.Driver
- url=jdbc:mysql://localhost:3306/mydb
- user=root
- password=root
2 加载properties配置文件
- public class JDBCUtils {
- private static String driver;
- private static String url;
- private static String user;
- private static String password;
- // 静态代码块
- static {
- try {
- // 1 使用Properties处理流
- // 使用load()方法加载指定的流
- Properties props = new Properties();
- Reader is = new FileReader("db.properties");
- props.load(is);
- // 2 使用getProperty(key),通过key获得需要的值,
- driver = props.getProperty("driver");
- url = props.getProperty("url");
- user = props.getProperty("user");
- password = props.getProperty("password");
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- /**
- * 获得连接
- */
- public static Connection getConnection() {
- try {
- // 1 注册驱动
- Class.forName(driver);
- // 2 获得连接
- Connection conn = DriverManager.getConnection(url, user, password);
- return conn;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
5 SQL 注入
输入一个存在的用户名 userName,再输入任意密码:XXX' OR 'a' = 'a'时,
SELECT * FROM Table WHERE Name = 'userName' AND PassWord = 'XXX' OR 'a' = 'a';
此时绕过了密码校验成功登录,这便是SQL注入问题。
为此,可以使用PreparedStatement将SQL预先进行编译,使用?作为占位符。当传入变量(包含SQL的关键字)时,不会识别 or 这些关键字。
- public class UserDao {
- public boolean login(String username,String password){
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- // 定义一个变量:
- boolean flag = false;
- try{
- conn = JDBCUtils.getConnection();
- String sql = "select * from user where username = ? and password = ?";
- // 预编译SQL
- pstmt = conn.prepareStatement(sql);
- // 设置参数:
- pstmt.setString(1, username);
- pstmt.setString(2, password);
- rs = pstmt.executeQuery();
- if(rs.next()){
- flag = true;
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- JDBCUtils.release(rs, pstmt, conn);
- }
- return flag;
- }
6 JDBC批处理
Statement接口:void addBatch(String sql)
PreparedStatement接口重写了addBatch()方法:void addBatch()
- public void demo1(){
- Connection conn = null;
- Statement stmt = null;
- try{
- // 获得连接:
- conn = JDBCUtils.getConnection();
- // 创建执行批处理对象:
- stmt = conn.createStatement();
- // 编写一批SQL语句:
- String sql1 = "create database test1";
- String sql2 = "use test1";
- String sql3 = "create table user(id int primary key auto_increment,name varchar(20))";
- String sql4 = "insert into user values (null,'aaa')";
- String sql5 = "insert into user values (null,'bbb')";
- String sql6 = "insert into user values (null,'ccc')";
- String sql7 = "update user set name = 'mmm' where id = 2";
- String sql8 = "delete from user where id = 1";
- // 添加到批处理
- stmt.addBatch(sql1);
- stmt.addBatch(sql2);
- stmt.addBatch(sql3);
- stmt.addBatch(sql4);
- stmt.addBatch(sql5);
- stmt.addBatch(sql6);
- stmt.addBatch(sql7);
- stmt.addBatch(sql8);
- // 执行批处理:
- stmt.executeBatch();
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- JDBCUtils.release(stmt, conn);
- }
- }
- //批量插入(使用PreparedStatement)
- @Test
- /**
- * 批量插入记录:
- * * 默认情况下MySQL批处理没有开启的,需要在url后面拼接一个参数即可。
- */
- public void demo2(){
- // 记录开始时间:
- long begin = System.currentTimeMillis();
- Connection conn = null;
- PreparedStatement pstmt = null;
- try{
- conn = JDBCUtils.getConnection();
- String sql = "insert into user values (null,?)";
- pstmt = conn.prepareStatement(sql);
- for(int i=1;i<=10000;i++){
- pstmt.setString(1, "name"+i);
- pstmt.addBatch();
- if(i % 1000 == 0){
- pstmt.executeBatch();
- pstmt.clearBatch();
- }
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- JDBCUtils.release(pstmt, conn);
- }
- long end = System.currentTimeMillis();
- System.out.println((end-begin));
- }
7 JDBC事务管理
例如在转账操作中,如果没有添加事务管理,可能出现 a b 账户互相转账后,两账户总额跟操作前不相等的情况,此时需要给转账功能添加事务管理。
- public void demo1(){
- Connection conn = null;
- PreparedStatement pstmt = null;
- try{
- conn = JDBCUtils.getConnection();
- // 1 开启事务
- conn.setAutoCommit(false);
- String sql = "update account set money = money + ? where name = ?";
- pstmt = conn.prepareStatement(sql);
- pstmt.setDouble(1, -1000);
- pstmt.setString(2, "aaa");
- pstmt.executeUpdate();
- int i = 1 / 0;
- pstmt.setDouble(1, 1000);
- pstmt.setString(2, "bbb");
- pstmt.executeUpdate();
- // 2 提交事务:
- conn.commit();
- }catch(Exception e){
- // 3 回滚事务:
- try {
- conn.rollback();
- } catch (SQLException e1) {
- e1.printStackTrace();
- }
- e.printStackTrace();
- }finally{
- JDBCUtils.release(pstmt, conn);
- }
- }
8 DBUtils简介
为简化JDBC开发,可以使用DBUtils,DBUtils封装了对JDBC的操作,是JDBC的简化开发工具包。使用需要导入commons-dbutils-1.6.jar。
Dbutils三个核心功能
QueryRunner 提供对sql语句操作的API,用来执行SQL语句的对象
ResultSetHandler接口 定义select后怎样封装结果集.
DbUtils类 工具类,定义了关闭资源与事务处理的方法
1 QueryRunner
无事务管理:
int update(String sql,Object… args);:增、删、改
T query(String sql,ResultSetHandler rsh,Object… args);:查询
有事务管理:
int update(Connection conn, String sql, Object... params) :增、删、改
T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) :查询
query(Connection con,String sql,ResultSetHandler r, Object..params)
ResultSetHandler r 结果集的处理方式,传递ResultSetHandler接口实现类
Object..params SQL语句中的?占位符
注意: query方法返回值,返回的是T 泛型, 具体返回值类型,跟随结果集处理方式变化
- //增加
- public void demo1() throws SQLException{
- // 创建核心类:QueryRunner:
- QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
- queryRunner.update("insert into account values (null,?,?)", "ddd",10000);
- }
- //删除
- public void demo3() throws SQLException{
- QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
- queryRunner.update("delete from account where id = ?", 3);
- }
- //修改
- public void demo2() throws SQLException{
- QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
- queryRunner.update("update account set name=?,money=? where id =?", "eee",20000,4);
- }
- //增加
- public void insert(){
- try {
- //获取一个QueryRunner对象,用来执行SQL语句
- QueryRunner queryRunner = new QueryRunner();
- String sql = "INSERT INTO zhangwu(name,money,parent) VALUES(?,?,?)";
- Object[] params = {"股票收入", 5500, "收入"};
- Connection conn = JDBCUtils.getConnection();
- int line = queryRunner.update(conn,sql,params);// 用来完成表数据的增加、删除、更新操作
- //结果集处理
- System.out.println("line = " + line);
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
- //删除
- public void delete(){
- try {
- QueryRunner queryRunner = new QueryRunner();
- String sql = "DELETE FROM zhangwu WHERE name = ?";
- Object[] params = {"股票收入"};
- Connection conn = JDBCUtils.getConnection();
- int line = queryRunner.update(conn, sql, params);
- System.out.println("line="+line);
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
- //修改
- public void update(){
- try {
- QueryRunner queryRunner = new QueryRunner();
- String sql = "UPDATE zhangwu SET money = money+1000 WHERE name=?";
- Object[] params = {"股票收入"};
- Connection conn = JDBCUtils.getConnection();
- int line = queryRunner.update(conn, sql, params);
- System.out.println("line="+line);
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
QueryRunner的两个构造方法
第一种:不带connection参数
QueryRunner queryRunner = new QueryRunner();
这种情况下,调用update或query方法时,需要传入对应的connection参数
queryRunner.update(conn, sql,params);
conn.close();
DBUtils调用这种带connection参数的方法时,
只会关闭preparedstatement和resultset对象,不会关闭conneciton对象,
一些情况下,没有手动关闭,可能会导致连接池满了,访问数据库是处于一直等待的状态。
就是为了其他方法来调用这个conneciton,所以这种连接数据库的方法适合操作事务。
第二种:带connection参数
QueryRunner queryRunner = new QueryRunner(dataSource);
将dataSource传递进去,这样update或query方法内部就调用this.getconnection方法来从这个数据源获得连接,
queryRunner.update( sql,params);
操作完后,就关闭conneciton,preparedstatement和resultset对象.
事务是自动控制的,一条SQL语句一个事务,不需要人为的控制。
2 ResultSetHandler
ArrayHandler 将结果集的第一条记录封装到Object[]数组中,数组中的每一个元素就是这条记录中每一个字段的值
ArrayListHandler 将结果集的每一条记录都封装到Object[]数组中,数组封装到List集合中。
BeanHandler 将结果集第一条记录封装到javaBean中。
BeanListHandler 将结果集每一条记录封装到javaBean中,javaBean封装到List集合中
ColumnListHandler 将结果集指定的列的字段值,封装到一个List集合中
ScalarHandler 用于单数据。例如select count(*) from 表操作。
MapHandler 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据
MapListHandler 将结果集第一行封装到Map集合中,Key 列名, Value 该列数据,Map集合存储到List集合
1)JavaBean
JavaBean就是一个类,在开发中常用封装数据。具有如下特性
1.需要实现接口:java.io.Serializable ,通常实现接口这步骤省略了,不会影响程序。
2.提供私有字段:private 类型 字段名;
3.提供getter/setter方法:
4.提供无参构造
示例:
- public class ZhangWu {
- private int id;
- private String name;
- private double money;
- private String parent;
- public ZhangWu() {
- super();
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public double getMoney() {
- return money;
- }
- public void setMoney(double money) {
- this.money = money;
- }
- public String getParent() {
- return parent;
- }
- public void setParent(String parent) {
- this.parent = parent;
- }
- @Override
- public String toString() { //该方法可以省略
- return "ZhangWu [id=" + id + ", name=" + name + ", money=" + money + ", parent=" + parent + "]";
- }
- }
3 ResultSetHandler使用案例
- public class ArrayHandlerDemo {
- public void method(){
- try {
- //获取QueryRunner对象
- QueryRunner qr = new QueryRunner();
- //执行SQL语句
- String sql = "SELECT * FROM zhangwu";
- Object[] params = {};
- Connection conn = JDBCUtils.getConnection();
- Object[] objArray = qr.query(conn, sql, new ArrayHandler(), params);
- //结果集的处理
- System.out.println( Arrays.toString(objArray) );
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- //ArrayListHandlerDemo
- String sql = "SELECT * FROM zhangwu WHERE money>?";
- Object[] params = {2000};
- Connection conn = JDBCUtils.getConnection();
- List<Object[]> list = qr.query(conn, sql, new ArrayListHandler(), params);
- for (Object[] objArray : list) {
- System.out.println( Arrays.toString(objArray) );
- }
- //BeanHandlerDemo
- String sql = "SELECT * FROM zhangwu WHERE id=?";
- Object[] params = {1};
- Connection conn = JDBCUtils.getConnection();
- ZhangWu zw = qr.query(conn, sql, new BeanHandler<ZhangWu>(ZhangWu.class), params);
- System.out.println(zw);
- //BeanListHandlerDemo
- String sql = "SELECT * FROM zhangwu WHERE money>?";
- Object[] params = {2000};
- Connection conn = JDBCUtils.getConnection();
- List<ZhangWu> list = qr.query(conn, sql, new BeanListHandler<ZhangWu>(ZhangWu.class), params);
- for (ZhangWu zw : list) {
- System.out.println(zw);
- }
- //ColumnListHandlerDemo
- String sql = "SELECT name FROM zhangwu WHERE money>?";
- Object[] params = {2000};
- Connection conn = JDBCUtils.getConnection();
- List<String> list = qr.query(conn, sql, new ColumnListHandler<String>(), params);
- for (String str : list) {
- System.out.println(str);
- }
- //ScalarHandlerDemo
- String sql = "SELECT MAX(money) FROM zhangwu";
- Object[] params = {};
- Connection conn = JDBCUtils.getConnection();
- Double max = qr.query(conn, sql, new ScalarHandler<Double>(), params);
- System.out.println("max=" + max);
- /*
- * 结果集第一种处理方法, ArrayHandler
- * 将结果集的第一行存储到对象数组中 Object[]
- */
- public static void arrayHandler()throws SQLException{
- QueryRunner qr = new QueryRunner();
- String sql = "SELECT * FROM sort";
- //调用方法query执行查询,传递连接对象,SQL语句,结果集处理方式的实现类
- //返回对象数组
- Object[] result = qr.query(con, sql, new ArrayHandler());
- for(Object obj : result){
- System.out.print(obj);
- }
- }
- /*
- * 结果集第二种处理方法,ArrayListHandler
- * 将结果集的每一行,封装到对象数组中, 出现很多对象数组
- * 对象数组存储到List集合
- */
- public static void arrayListHandler()throws SQLException{
- QueryRunner qr = new QueryRunner();
- String sql = "SELECT * FROM sort";
- //调用query方法,结果集处理的参数上,传递实现类ArrayListHandler
- //方法返回值 每行是一个对象数组,存储到List
- List<Object[]> result= qr.query(con, sql, new ArrayListHandler());
- //集合的遍历
- for( Object[] objs : result){
- //遍历对象数组
- for(Object obj : objs){
- System.out.print(obj+" ");
- }
- System.out.println();
- }
- }
- /*
- * 结果集第三种处理方法,BeanHandler
- * 将结果集的第一行数据,封装成JavaBean对象
- * 注意: 被封装成数据到JavaBean对象, Sort类必须有空参数构造
- */
- public static void beanHandler()throws SQLException{
- QueryRunner qr = new QueryRunner();
- String sql = "SELECT * FROM sort ";
- //调用方法,传递结果集实现类BeanHandler
- //BeanHandler(Class<T> type)
- Sort s = qr.query(con, sql, new BeanHandler<Sort>(Sort.class));
- System.out.println(s);
- }
- /*
- * 结果集第四种处理方法, BeanListHandler
- * 结果集每一行数据,封装JavaBean对象
- * 多个JavaBean对象,存储到List集合
- */
- public static void beanListHander()throws SQLException{
- QueryRunner qr = new QueryRunner();
- String sql = "SELECT * FROM sort ";
- //调用方法query,传递结果集处理实现类BeanListHandler
- List<Sort> list = qr.query(con, sql, new BeanListHandler<Sort>(Sort.class));
- for(Sort s : list){
- System.out.println(s);
- }
- }
- /*
- * 结果集第五种处理方法,ColumnListHandler
- * 结果集,指定列的数据,存储到List集合
- * List<Object> 每个列数据类型不同
- */
- public static void columnListHandler()throws SQLException{
- QueryRunner qr = new QueryRunner();
- String sql = "SELECT * FROM sort ";
- //调用方法 query,传递结果集实现类ColumnListHandler
- //实现类构造方法中,使用字符串的列名
- List<Object> list = qr.query(con, sql, new ColumnListHandler<Object>("sname"));
- for(Object obj : list){
- System.out.println(obj);
- }
- }
- /*
- * 结果集第六种处理方法,ScalarHandler
- * 对于查询后,只有1个结果
- */
- public static void scalarHandler()throws SQLException{
- QueryRunner qr = new QueryRunner();
- String sql = "SELECT COUNT(*) FROM sort";
- //调用方法query,传递结果集处理实现类ScalarHandler
- long count = qr.query(con, sql, new ScalarHandler<Long>());
- System.out.println(count);
- }
- /*
- * 结果集第七种处理方法,MapHandler
- * 将结果集第一行数据,封装到Map集合中
- * Map<键,值> 键:列名 值:这列的数据
- */
- public static void mapHandler()throws SQLException{
- QueryRunner qr = new QueryRunner();
- String sql = "SELECT * FROM sort";
- //调用方法query,传递结果集实现类MapHandler
- //返回值: Map集合,Map接口实现类, 泛型
- Map<String,Object> map = qr.query(con, sql, new MapHandler());
- //遍历Map集合
- for(String key : map.keySet()){
- System.out.println(key+".."+map.get(key));
- }
- }
- /*
- * 结果集第八种处理方法,MapListHandler
- * 将结果集每一行存储到Map集合,键:列名,值:数据
- * Map集合过多,存储到List集合
- */
- public static void mapListHandler()throws SQLException{
- QueryRunner qr = new QueryRunner();
- String sql = "SELECT * FROM sort";
- //调用方法query,传递结果集实现类MapListHandler
- //返回值List集合, 存储的是Map集合
- List<Map<String,Object>> list = qr.query(con, sql, new MapListHandler());
- //遍历集合list
- for( Map<String,Object> map : list ){
- for(String key : map.keySet()){
- System.out.print(key+"..."+map.get(key));
- }
- System.out.println();
- }
- }
9 连接池
9.1 DBCP连接池
Java提供了数据库连接池公共接口:javax.sql.DataSource,各厂商需要让自己的连接池实现这个接口,这样程序可以方便地切换不同厂商的连接池。
DBCP是tomcat内置开源连接池,提供了DataSource接口的实现类:BasicDataSource类
需要的jar包:
mysql-connector-java-5.1.37-bin.jar:数据库驱动
commons-dbutils-1.6.jar:提供QueryRunner类方便进行增删改查操作
commons-dbcp-1.4.jar:
commons-pool-1.5.6.jar:提供高效的数据库连接池技术
BasicDataSource类的使用:
- //使用DBCP实现数据库的连接池
- public class JDBCUtils{
- //创建出BasicDataSource类对象
- private static BasicDataSource datasource = new BasicDataSource();
- static{
- datasource.setDriverClassName("com.mysql.jdbc.Driver");
- datasource.setUrl("jdbc:mysql://localhost:3306/day33_user");
- datasource.setUsername("root");
- datasource.setPassword("123");
- datasource.setInitialSize(10);//初始化的连接数
- datasource.setMaxActive(8);//最大连接数量
- datasource.setMaxIdle(5);//最大空闲数
- datasource.setMinIdle(1);//最小空闲
- }
- //定义静态方法,返回BasicDataSource类的对象
- public static DataSource getDataSource(){
- return datasource;
- }
- }
常见配置
必须项
driverClassName 数据库驱动名称
url 数据库的地址
username 用户名
password 密码
基本项(扩展)
maxActive 最大连接数量
minIdle 最小空闲连接
maxIdle 最大空闲连接
initialSize 初始化连接
测试连接池:
- /*
- * 测试写好的工具类,
- * 提供的是一个DataSource接口的数据源
- * QueryRunner类构造方法,接收DataSource接口的实现类
- * 后面,调用方法update,query,无需传递他们Connection连接对象
- */
- public class QueryRunnerDemo{
- public static void main(String[] args) {
- select();
- }
- //定义2个方法,实现数据表的添加,数据表查询
- //QueryRunner类对象,写在类成员位置
- private static QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
- //数据表查询
- public static void select(){
- String sql = "SELECT * FROM sort";
- try{
- List<Object[]> list = qr.query(sql, new ArrayListHandler());
- for(Object[] objs : list){
- for(Object obj : objs){
- System.out.print(obj+"\t");
- }
- System.out.println();
- }
- }catch(SQLException ex){
- throw new RuntimeException("数据查询失败");
- }
- }
- }
9.2 C3P0连接池
C3P0是一个开源JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
C3P0与DBCP区别:DBCP没有自动回收空闲连接的功能,C3P0有
C3P0连接池的使用:
- /**
- * 1 手动设置参数的方式:
- */
- public void demo1(){
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- try{
- // 获得连接:从连接池中获取:
- // 创建连接池:
- ComboPooledDataSource dataSource = new ComboPooledDataSource();
- // 设置连接参数:
- dataSource.setDriverClass("com.mysql.jdbc.Driver");
- dataSource.setJdbcUrl("jdbc:mysql:///web_test4");
- dataSource.setUser("root");
- dataSource.setPassword("abc");
- conn = dataSource.getConnection();
- String sql = "select * from account";
- pstmt = conn.prepareStatement(sql);
- rs = pstmt.executeQuery();
- while(rs.next()){
- System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- JDBCUtils.release(rs, pstmt, conn);
- }
- }
- /**
- * 2 采用配置文件的方式:
- */
- public void demo2(){
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- try{
- // 获得连接:从连接池中获取:
- // 创建连接池://创建连接池默认去类路径下查找c3p0-config.xml
- ComboPooledDataSource dataSource = new ComboPooledDataSource();
- // 从连接池中获得连接:
- conn = dataSource.getConnection();
- String sql = "select * from account";
- pstmt = conn.prepareStatement(sql);
- rs = pstmt.executeQuery();
- while(rs.next()){
- System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- JDBCUtils.release(rs, pstmt, conn);
- }
- }
- <?xml version="1.0" encoding="UTF-8"?>
- <c3p0-config>
- <!-- c3p0-config.xml -->
- <default-config>
- <property name="driverClass">com.mysql.jdbc.Driver</property>
- <property name="jdbcUrl">jdbc:mysql:///web_test4</property>
- <property name="user">root</property>
- <property name="password">abc</property>
- <property name="initialPoolSize">5</property>
- <property name="minPoolSize">5</property>
- <property name="maxPoolSize">20</property>
- </default-config>
- </c3p0-config>
C3P0详细配置:
- <?xml version="1.0" encoding="gbk"?>
- <c3p0-config>
- <!--默认配置,如果获取连接池时没有指定名称,则使用默认配置信息-->
- <default-config>
- <!--连接url-->
- <property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property>
- <!--数据库驱动类-->
- <property name="driverClass">com.mysql.jdbc.Driver</property>
- <!--用户名。Default: null-->
- <property name="user">root</property>
- <!--密码。Default: null-->
- <property name="password"></property>
- <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
- <property name="acquireIncrement">3</property>
- <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
- <property name="acquireRetryAttempts">30</property>
- <!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
- <property name="acquireRetryDelay">1000</property>
- <!--连接关闭时默认将所有未提交的操作回滚。Default: false -->
- <property name="autoCommitOnClose">false</property>
- <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么
- 属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试
- 使用。Default: null-->
- <property name="automaticTestTable">Test</property>
- <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
- 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
- 获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->
- <property name="breakAfterAcquireFailure">false</property>
- <!--当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出
- SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
- <property name="checkoutTimeout">100</property>
- <!--通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。
- Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
- <property name="connectionTesterClassName"></property>
- <!--指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可
- Default: null-->
- <property name="factoryClassLocation">null</property>
- <!--Strongly disrecommended. Setting this to true may lead to subtle and bizarre bugs.
- (文档原文)作者强烈建议不使用的一个属性-->
- <property name="forceIgnoreUnresolvedTransactions">false</property>
- <!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
- <property name="idleConnectionTestPeriod">60</property>
- <!--初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
- <property name="initialPoolSize">3</property>
- <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
- <property name="maxIdleTime">60</property>
- <!--连接池中保留的最大连接数。Default: 15 -->
- <property name="maxPoolSize">15</property>
- <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
- 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
- 如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
- <property name="maxStatements">100</property>
- <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
- <property name="maxStatementsPerConnection"></property>
- <!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能
- 通过多线程实现多个操作同时被执行。Default: 3-->
- <property name="numHelperThreads">3</property>
- <!--当用户调用getConnection()时使root用户成为去获取连接的用户。主要用于连接池连接非c3p0
- 的数据源时。Default: null-->
- <property name="overrideDefaultUser">root</property>
- <!--与overrideDefaultUser参数对应使用的一个参数。Default: null-->
- <property name="overrideDefaultPassword">password</property>
- <!--定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意:
- 测试的表必须在初始数据源的时候就存在。Default: null-->
- <property name="preferredTestQuery">select id from test where id=1</property>
- <!--用户修改系统配置参数执行前最多等待300秒。Default: 300 -->
- <property name="propertyCycle">300</property>
- <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
- 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
- 等方法来提升连接测试的性能。Default: false -->
- <property name="testConnectionOnCheckout">false</property>
- <!--如果设为true那么在取得连接的同时将校验连接的有效性。Default: false -->
- <property name="testConnectionOnCheckin">true</property>
- <!--早期的c3p0版本对JDBC接口采用动态反射代理。在早期版本用途广泛的情况下这个参数
- 允许用户恢复到动态反射代理以解决不稳定的故障。最新的非反射代理更快并且已经开始
- 广泛的被使用,所以这个参数未必有用。现在原先的动态反射与新的非反射代理同时受到
- 支持,但今后可能的版本可能不支持动态反射代理。Default: false-->
- <property name="usesTraditionalReflectiveProxies">false</property>
- </default-config>
- <!--指定配置名称的配置信息-->
- <named-config name="dumbTestConfig">
- </named-config>
- </c3p0-config>
9.3 Druid连接池
阿里旗下开源连接池产品,使用简单,可以与Spring框架进行快速整合
1 Druid的使用:
- /**
- * 1 手动设置参数的方式
- */
- public void demo1(){
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- try{
- // 使用连接池:
- DruidDataSource dataSource = new DruidDataSource();
- // 手动设置数据库连接的参数:
- dataSource.setDriverClassName("com.mysql.jdbc.Driver");
- dataSource.setUrl("jdbc:mysql:///web_test4");
- dataSource.setUsername("root");
- dataSource.setPassword("abc");
- // 获得连接:旧方式
- //conn = JDBCUtils.getConnection();
- conn = dataSource.getConnection();
- String sql = "select * from account";
- pstmt = conn.prepareStatement(sql);
- rs = pstmt.executeQuery();
- while(rs.next()){
- System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- JDBCUtils.release(rs, pstmt, conn);
- }
- }
- /**
- * 2 配置方式设置参数
- * Druid配置方式可以使用属性文件配置的。
- * 文件名称没有规定但是属性文件中的key要一定的。
- */
- public void demo2(){
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- try{
- // 使用连接池:
- // 从属性文件中获取:
- Properties properties = new Properties();
- properties.load(new FileInputStream("src/druid.properties"));
- DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
- // 获得连接:旧方式
- //conn = JDBCUtils.getConnection();
- conn = dataSource.getConnection();
- String sql = "select * from account";
- pstmt = conn.prepareStatement(sql);
- rs = pstmt.executeQuery();
- while(rs.next()){
- System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- JDBCUtils.release(rs, pstmt, conn);
- }
- }
2 Druid监控及慢sql记录:
https://www.cnblogs.com/telwanggs/p/7484854.html
9.4 自定义连接池
步骤:
1 编写一个类实现DataSource接口
2 重写getConnection方法
3 初始化多个连接在内存中
4 编写归还连接的方法
- //自定义连接池
- public class MyDataSource implements DataSource {
- // 将一些连接存入到内存中,可以定义一个集合,用于存储连接对象。
- private List<Connection> connList = new ArrayList<Connection>();
- // 在初始化的时候提供一些连接
- public MyDataSource() {
- // 初始化连接:
- for(int i = 1;i<=3;i++){
- // 向集合中存入连接:
- connList.add(JDBCUtils.getConnection());
- }
- }
- // 从连接池中获得连接的方法
- @Override
- public Connection getConnection() throws SQLException {
- Connection conn = connList.remove(0);
- // 增强连接:
- MyConnectionWrapper connWrapper = new MyConnectionWrapper(conn, connList);
- return connWrapper;
- }
- // 编写一个归还连接的方法:
- /*public void addBack(Connection conn){
- connList.add(conn);
- }*/
- @Override
- public PrintWriter getLogWriter() throws SQLException {
- // TODO Auto-generated method stub
- return null;
- }
- @Override
- public int getLoginTimeout() throws SQLException {
- // TODO Auto-generated method stub
- return 0;
- }
- @Override
- public Logger getParentLogger() throws SQLFeatureNotSupportedException {
- // TODO Auto-generated method stub
- return null;
- }
- @Override
- public void setLogWriter(PrintWriter arg0) throws SQLException {
- // TODO Auto-generated method stub
- }
- @Override
- public void setLoginTimeout(int arg0) throws SQLException {
- // TODO Auto-generated method stub
- }
- @Override
- public boolean isWrapperFor(Class<?> arg0) throws SQLException {
- // TODO Auto-generated method stub
- return false;
- }
- @Override
- public <T> T unwrap(Class<T> arg0) throws SQLException {
- // TODO Auto-generated method stub
- return null;
- }
- @Override
- public Connection getConnection(String arg0, String arg1) throws SQLException {
- // TODO Auto-generated method stub
- return null;
- }
- }
测试自定义连接池:
- public class DataSourceDemo1 {
- @Test
- /**
- * 测试自定义连接池
- */
- public void demo1(){
- Connection conn = null;
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- DataSource dataSource = null;
- try{
- // 获得连接:
- // conn = JDBCUtils.getConnection();
- // 从连接池中获得连接:
- dataSource = new MyDataSource();
- conn = dataSource.getConnection();
- String sql = "select * from account";
- pstmt = conn.prepareStatement(sql);
- rs = pstmt.executeQuery();
- while(rs.next()){
- System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- JDBCUtils.release(rs, pstmt, conn);
- // 归还连接:
- // dataSource.addBack(conn);
- }
- }
- }
使用装饰者增强Connection中的close方法:将原有的close方法的逻辑改为归还(增强一个类中的方法)
装饰者模式使用条件:
1、增强类和被增强类实现相同的接口
2、在增强的类中获得被增强的类的引用
- public class MyConnectionWrapper extends ConnectionWrapper{
- private Connection conn;
- private List<Connection> connList;
- public MyConnectionWrapper(Connection conn,List<Connection> connList) {
- super(conn);
- this.conn = conn;
- this.connList= connList;
- }
- // 增强某个方法:
- @Override
- public void close() throws SQLException {
- // super.close();
- // 归还连接:
- connList.add(conn);
- }
- }
JDBC 和连接池的更多相关文章
- c3p0、dbcp、tomcat jdbc pool 连接池配置简介及常用数据库的driverClass和驱动包
[-] DBCP连接池配置 dbcp jar包 c3p0连接池配置 c3p0 jar包 jdbc-pool连接池配置 jdbc-pool jar包 常用数据库的driverClass和jdbcUrl ...
- jdbc数据连接池dbcp要导入的jar包
jdbc数据连接池dbcp要导入的jar包 只用导入commons-dbcp-x.y.z.jarcommons-pool-a.b.jar
- 关于JDBC和连接池我学到的(转载保存)
1.JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模 ...
- JDBC之 连接池
JDBC之 连接池 有这样的一种现象: 用java代码操作数据库,需要数据库连接对象,一个用户至少要用到一个连接.现在假设有成千上百万个用户,就要创建十分巨大数量的连接对象,这会使数据库承受极大的压力 ...
- JDBC数据源连接池(4)---自定义数据源连接池
[续上文<JDBC数据源连接池(3)---Tomcat集成DBCP>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究 ...
- JDBC数据源连接池(3)---Tomcat集成DBCP
此文续<JDBC数据源连接池(2)---C3P0>. Apache Tomcat作为一款JavaWeb服务器,内置了DBCP数据源连接池.在使用中,只要进行相应配置即可. 首先,确保Web ...
- JDBC数据源连接池(2)---C3P0
我们接着<JDBC数据源连接池(1)---DBCP>继续介绍数据源连接池. 首先,在Web项目的WebContent--->WEB-INF--->lib文件夹中添加C3P0的j ...
- DBCP,C3P0与Tomcat jdbc pool 连接池的比较
hibernate开发组推荐使用c3p0; spring开发组推荐使用dbcp(dbcp连接池有weblogic连接池同样的问题,就是强行关闭连接或数据库重启后,无法reconnect,告诉连接被重置 ...
- JDBC数据源连接池的配置和使用实例
个人学习参考所用,勿喷! 使用JDBC建立数据库连接的两种方式: 1.在代码中使用DriverManager获得数据库连接.这种方式效率低,并且其性能.可靠性和稳定性随着用户访问量得增加逐渐下降. 2 ...
- mysql,jdbc、连接池
show processlist; select * from information_schema.processlist; Command: The type of command the thr ...
随机推荐
- ubuntu设置分辨率
前言 装过ubuntu的虚拟机人应该都知道,刚刚装完系统时,分辨率小的令人发指,根本就不能愉快的使用,所以必须调整,但是有些分辨率ubuntu里面也没有,这就需要我们自己自定义. 自定义分辨率 1. ...
- vultr搭建ss
我在Ubuntu1604上运行的sslocal,但是发现firefox无法链接ss代理,后来用的chromium才成功连接上, ---------------------------- 下面是正文 - ...
- open-falcon实现邮件报警
1.请安装好Go的环境,参考上一篇open-falcon的安装博文 2.安装 mail-provider https://github.com/open-falcon/mail-provider 安装 ...
- Window应急响应(五):ARP病毒
0x00 前言 ARP病毒并不是某一种病毒的名称,而是对利用arp协议的漏洞进行传播的一类病毒的总称,目前在局域网中较为常见.发作的时候会向全网发送伪造的ARP数据包,严重干扰全网的正常运行,其危害甚 ...
- linux telnet检测与某个端口是否开通
转自:http://blog.51cto.com/meiling/1982402 一:telnet此法常被用来检测是个远端端口是否通畅. 测试域名: # telnet baidu.com 80 Try ...
- 卸载ie
今天卸载ie11失败,最后使用下面这个命令实现了卸载,记录下 IE11卸载命令如下: FORFILES /P %WINDIR%\servicing\Packages /M Microsoft-Wind ...
- 四、Sql Server 基础培训《进度4-插入数据(实际操作)》
知识点: 假设有订单表 CREATE TABLE Order ( ID int identity(1,1) not null primary key, --内码 BillNo varchar(100) ...
- 【Zookeeper系列】Zookeeper简单介绍(转)
原文链接:https://www.cnblogs.com/sunddenly/p/4033574.html 一.分布式协调技术 在给大家介绍ZooKeeper之前先来给大家介绍一种技术——分布式协调技 ...
- CF 1131C Birthday
C. Birthday time limit per test 1 second memory limit per test 256 megabytes input standard input ou ...
- 线程的条件Condiition
条件Condition相当于给锁造钥匙,但是这钥匙是一次性的.一个线程拿到钥匙进去,出来之后钥匙没有归还,而是没了. 如下代码: from threading import Thread, Condi ...