jdbc说明
什么是jdbc?(Java Data Base Connectivity,java数据库连接),由一些接口和类构成的API
步骤
- 注册驱动 (只做一次)
- 建立连接(Connection)
- 创建执行SQL的语句(Statement)
- 执行语句
- 处理执行结果(ResultSet)
- 释放资源
注册驱动
- Class.forName(“com.mysql.jdbc.Driver”); 推荐这种方式,不会对具体的驱动类产生依赖。
- DriverManager.registerDriver(com.mysql.jdbc.Driver); 会造成DriverManager中产生两个一样的驱动,并会对具体的驱动类产生依赖。
- System.setProperty(“jdbc.drivers”, “driver1:driver2”); 虽然不会对具体的驱动类产生依赖;但注册不太方便,所以很少使用。
- 驱动类型(四种类型)
建立连接(Connection)
- Connection conn = DriverManager.getConnection(url, user, password);
- url格式: JDBC:子协议:子名称//主机名:端口/数据库名?属性名=属性值&…
- User,password可以用“属性名=属性值”方式告诉数据库;
- 其他参数如:useUnicode=true&characterEncoding=GBK。
创建执行SQL的语句(Statement)
- Statement
Statement st = conn.createStatement();
st.executeQuery(sql);
- PreparedStatement
String sql = “select * from table_name where col_name=?”;
PreparedStatement ps = conn.preparedStatement(sql);
ps.setString(1, “col_value”);
ps.executeQuery();
处理执行结果(ResultSet)
ResultSet rs = statement.executeQuery(sql);
While(rs.next()){
rs.getString(“col_name”);
rs.getInt(“col_name”);
//…
}
释放资源
- 释放ResultSet, Statement,Connection.
- 数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
基本的CRUD(创建、读取、更新、删除)
- 模板代码
Connection conn = null;
Statement st=null;
ResultSet rs = null;
try {
//获得Connection
//创建Statement
//处理查询结果ResultSet
} finally {
//释放资源ResultSet, Statement,Connection
}
创建
- 增加对应SQL的INSERT,返回增加成功的行(记录)数
conn = getConnection();
Statement st = conn.createStatement();
String sql=“insert into user(name, age,regist_date )” + “values(‘name’, 10, now())”;
int i = st.executeUpdate(sql);
//i为插入的记录数
读取
- 读取(查询)对应SQL的SELECT,返回查询结果
conn = getConnection();
st = conn.createStatement();
String sql = "select id, name, age,regist_date from user";
rs = st.executeQuery(sql);
while (rs.next()) {
System.out.print(rs.getInt("id") + " \t\t ");
System.out.print(rs.getString("name") + " \t\t ");
System.out.print(rs.getInt("age") + " \t\t ");
System.out.print(rs.getTimestamp("regist_date") + " \t\t ");
System.out.println();
}
更新
- 更新(修改)对应SQL的UPDATE,返回被修改的行(记录)数
conn = getConnection();
Statement st = conn.createStatement();
String sql=“update person set name='new name‘”;
int i = st.executeUpdate(sql);
//i为符合条件的记录数
删除
- 删除对应SQL的DELETE,返回被删除的行(记录)数
conn = getConnection();
Statement st = conn.createStatement();
String sql=“delete from user where id=1”;
int i = st.executeUpdate(sql);
//i为删掉的记录数
SQL注入,PreparedStatement和Statement
- 在SQL中包含特殊字符或SQL的关键字(如:' or 1 or ')时Statement将出现不可预料的结果(出现异常或查询的结果不正确),可用PreparedStatement来解决。
- PreperedStatement(从Statement扩展而来)相对Statement的优点: 1.没有SQL注入的问题。 2.Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。 3.数据库和驱动可以对PreperedStatement进行优化(只有在相关联的数据库连接没有关闭的情况下有效)。
数据类型
-
详细信息见java.sql.Types
-
几种特殊且比较常用的类型
-
DATA,TIME,TIMESTAMP date,time,datetime
存:ps.setDate(i,d); ps.setTime(i,t); ps.setTimestamp(i, ts);
取:rs.getDate(i); rs.getTime(i); rs.getTimestamp(i);
-
CLOB text
存:ps.setCharacterStream(index, reader, length); ps.setString(i, s);
取:reader = rs. getCharacterStream(i); reader = rs.getClob(i).getCharacterStream(); string = rs.getString(i);
-
BLOB blob
存:ps.setBinaryStream(i, inputStream, length);
取:rs.getBinaryStream(i); rs.getBlob(i).getBinaryStream();
-
事务(ACID)
- 原子性(atomicity):组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。
- 一致性(consistency):在事务处理执行前后,数据库是一致的(两个账户要么都变,或者都不变)。
- 隔离性(isolcation):一个事务处理对另一个事务处理没有影响。
- 持续性(durability):事务处理的效果能够被永久保存下来 。
- connection.setAutoCommit(false);//打开事务。
- connection.commit();//提交事务。
- connection.rollback();//回滚事务。
隔离级别多线程并发读取数据时的正确性
- connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
- V:可能出现,X:不会出现
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(Read uncommitted) | V | V | V |
读已提交(Read committed) | X | V | V |
可重复读(Repeatable read) | X | X | V |
可串行化(Serializable ) | X | X | X |
存储过程
- 存储过程
CallableStatement(从PreperedStatement扩展来)
cs = connection.prepareCall(“{call psname(?,?,?)}”);
cs.registerOutParameter(index, Types.INTEGER);
cs.setXXX(i, xxxx);
cs.executeUpdate();
int id=cs.getInt(index);
其他的几个API
- Statement.getGeneratedKeys()
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.executeUpdate();
ResultSet rs = st.getGeneratedKeys();rs.getInt(1);
- 批处理,可以大幅度提升大量增、删、改的速度。
PreparedStatement.addBatch();
PreparedStatement.executeBatch();
- 可滚动的结果集
Statement st =
connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = st.executeQuery(sql);
rs.beforeFirst(); rs.afterLast();rs.first();rs.isFirst();rs.last();rs.isLast();
rs.absolute(9);rs.moveToInsertRow();
DatabaseMetaData
- DatabaseMetaData meta = connection.getMetaData();
- 通过DatabaseMetaData可以获得数据库相关的信息如:数据库版本、数据库名、数据库厂商信息、是否支持事务、是否支持某种事务隔离级别,是否支持滚动结果集等。
ResultSetMetaData
-
ResultSetMetaData meta = rs.getMetaData();
-
通过ResultSetMetaData可以获得结果有几列、各列名、各列别名、各列类型等。
-
可以将ResultSet放入Map(key:列名 value:列值)。
-
用反射ResultSetMetaData将查询结果读入对象中(简单的O/RMapping)
1)让SQL语句中列别名和要读入的对象属性名一样;
2)通过ResultSetMetaData获得结果列数和列别名;
3)通过反射将对象的所有setXxx方法找到;
4)将3)找到的方法setXxx和2)找到的列别名进行匹配(即方法中的xxx于列别名相等);
5)由上一步找到的方法和列别名对应关系进行赋值 Method.invoke(obj, rs.getObject(columnAliasName));
数据源和连接池
- DataSource用来取代DriverManager来获取Connection;
- 通过DataSource获得Connection速度很快;
- 通过DataSource获得的Connection都是已经被包裹过的(不是驱动原来的连接),他的close方法已经被修改。
- 一般DataSource内部会用一个连接池来缓存Connection,这样可以大幅度提高数据库的访问速度;
- 连接池可以理解成一个能够存放Connection的Collection;
- 我们的程序只和DataSource打交道,不会直接访问连接池;
最后
作者:ricky
交流群: 244930845