JDBC
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问
通过数据库厂家提供的Driver接口,连接数据库,通过不同数据库的驱动操作数据库
JDBC中的接口
常用接口:
1.Driver接口:用于注册驱动
装载MySql驱动:Class.forName("com.mysql.jdbc.Driver"); 利用反射注册驱动
装载MySql驱动:Class.forName("com.mysql.jdbc.Driver");
2.Connection接口:与特定数据库的连接对象
3.Statement接口:用于执行静态SQL语句并返回它所生成结果的对象
三种Statement类:
Statement:由Connection接口的createStatement方法创建,用于发送简单的SQL语句(不带参数)
PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。
PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。
CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程
4.ResultSet接口:SQL语句结果集对象,提供对结果集处理的方法
使用后依次关闭对象及连接:ResultSet → Statement → Connection
使用JDBC的步骤
1.注册驱动(只注册一次)
2.建立数据库连接,需要数据库连接的参数
3.创建执行SQL语句的对象statement
sql语句的增删改一般使用executeUpdate(sql)方法
查询一般是executeQuery(sql)方法
4.处理执行结果
5.释放资源
public static void main ( String[ ] args) throws ClassNotFoundException, SQLException {
Class. forName ( "com.mysql.jdbc.Driver" ) ;
String url = "jdbc:mysql://localhost:3306/test" ;
String user = "root" ;
String password = "123" ;
Connection conn = DriverManager. getConnection ( url, user, password) ;
Statement create = conn. createStatement ( ) ;
ResultSet res = create. executeQuery ( "select * from sort" ) ;
while ( res. next ( ) ) {
System. out. println ( res. getInt ( "sid" ) + " " + res. getString ( "names" ) + " " + res. getDouble ( "price" ) + " "
+ res. getString ( "descs" ) ) ;
}
res. close ( ) ;
create. close ( ) ;
conn. close ( ) ;
}
SQL注入
select * from person where name=? and password=?
有恶意用户输入参数1=1 or 等类似的参数让条件判断恒成立
所以不能使用字符串拼接将参数和SQL拼接
而是使用预编译的方式将参数以占位符的形式传入SQL中
使用预编译需要SQL操作对象是PreparedStatement(Statement接口的子类)
Class. forName ( "com.mysql.jdbc.Driver" ) ;
String url = "jdbc:mysql://localhost:3306/test" ;
String user = "root" ;
String password = "123" ;
Connection conn = DriverManager. getConnection ( url, user, password) ;
String sql = "select * from sort where age > ?" ;
PreparedStatement prepare = conn. prepareStatement ( sql) ;
prepare. setObject ( 1 , 12 ) ;
ResultSet res = prepare. executeQuery ( "select * from sort" ) ;
while ( res. next ( ) ) {
System. out. println ( res. getInt ( "sid" ) + " " + res. getString ( "names" ) + " " + res. getDouble ( "price" ) + " "
+ res. getString ( "descs" ) ) ;
}
res. close ( ) ;
prepare. close ( ) ;
conn. close ( ) ;
封装功能代码
数据库连接代码与资源关闭代码不会任意改变,可以抽取到工具类中
将数据库连接对象作为属性,使用静态代码块完成实例化
使用静态方法获取这个属性
使用静态方法关闭资源
public class JDBCutils {
private static Connection conn;
static {
try {
Class. forName ( "com.mysql.jdbc.Driver" ) ;
String url = "jdbc:mysql://localhost:3306/test" ;
String user = "root" ;
String password = "123" ;
conn = DriverManager. getConnection ( url, user, password) ;
System. out. println ( conn) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
public static Connection getConnection ( ) {
return conn;
}
public static void closeConnection ( Connection con, Statement s, ResultSet r) {
if ( con != null) {
try {
con. close ( ) ;
} catch ( SQLException e) {
e. printStackTrace ( ) ;
}
}
if ( s != null) {
try {
s. close ( ) ;
} catch ( SQLException e) {
e. printStackTrace ( ) ;
}
}
if ( r != null) {
try {
r. close ( ) ;
} catch ( SQLException e) {
e. printStackTrace ( ) ;
}
}
}
}
获取连接对象与关闭资源就可以直接调用工具类
public static void main ( String[ ] args) throws ClassNotFoundException, SQLException {
Connection conn = JDBCutils. getConnection ( ) ;
PreparedStatement pre = conn. prepareStatement ( "select names from sort" ) ;
ResultSet executeQuery = pre. executeQuery ( ) ;
while ( executeQuery. next ( ) ) {
System. out. println ( executeQuery. getString ( "names" ) ) ;
}
JDBCutils. closeConnection ( conn, pre, executeQuery) ;
}
关于数据库连接信息可以进一步封装为db.properties配置文件
将数据库连接需要的信息以键值形式写入配置文件
以Properties读取配置文件
public class JDBCPropertiesUtils {
private static String driver;
private static String url;
private static String user;
private static String password;
private static Connection conn;
static {
try {
readConfig ( ) ;
Class. forName ( driver) ;
conn = DriverManager. getConnection ( url, user, password) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
public static void readConfig ( ) throws IOException {
InputStream resour = JDBCProperties. class . getClassLoader ( ) . getResourceAsStream ( "db.properties" ) ;
Properties p = new Properties ( ) ;
p. load ( resour) ;
resour. close ( ) ;
driver = p. getProperty ( "driver" ) ;
url = p. getProperty ( "url" ) ;
user = p. getProperty ( "user" ) ;
password = p. getProperty ( "password" ) ;
}
JDBC的事务
设计数据库表的增删改操作时需要开启事务来配合
JDBC默认的事务处理行为是自动提交
JDBC是通过Connection对象进行事务管理
Connection.setAutoCommit() 设置自动提交,true为自动提交,false为手动提交
Connection.setAutoCommit(false) ;手动提交
SQL语句
Connection.commit();提交事务
Connection.rollback() 发生异常回滚事务
JDBC连接池
数据库连接的创建,销毁都非常销毁资源,可以防止线程池造一个数据库的连接池,存放一定数量的数据库连接
自定义数据池需要实现javax.sql.DataSource连接池接口
使用List集合存放多个连接的对象
初始时创建指定个数的连接对象放入连接池
获取连接时从连接池移除一个连接并返回给调用者
销毁连接时将连接添加到连接池(不是真正的销毁)
public class MyDataSource implements DataSource {
private static LinkedList< Connection> pool = new LinkedList < Connection> ( ) ;
static {
for ( int i = 0 ; i < 5 ; i++ ) {
Connection conn = JDBCUtils. getConnection ( ) ;
pool. add ( conn) ;
}
}
@Override
public Connection getConnection ( ) throws SQLException {
Connection conn = null;
if ( pool. size ( ) == 0 ) {
for ( int i = 0 ; i < 5 ; i++ ) {
conn = JDBCUtils. getConnection ( ) ;
pool. add ( conn) ;
}
}
conn = pool. remove ( 0 ) ;
return conn;
}
public void backConnection ( Connection conn) {
pool. add ( conn) ;
}
对Connection连接对象的close进行增强,当执行close方法时默认去执行连接池的返回对象到连接池的方法
自定义MyConnection实现Connection
重写close方法
在连接池中创建MyConnection对象代替Connection
public class MyConnection implements Connection {
private Connection conn;
private List< Connection> list;
public MyConnection ( Connection conn, List< Connection> list) {
this . conn = conn;
this . list = list;
}
@Override
public void close ( ) throws SQLException {
list. add ( conn) ;
}
连接池创建连接对象
public class MyDataSource implements DataSource {
@Override
public Connection getConnection ( ) throws SQLException {
if ( list. size ( ) <= 0 ) {
for ( int i= 1 ; i<= 3 ; i++ ) {
Connection conn = JDBCUtils. getConnection ( ) ;
MyConnection myConn = new MyConnection ( conn, list) ;
list. add ( myConn) ;
}
}
Connection conn = list. remove ( 0 ) ;
MyConnection myConn = new MyConnection ( conn, list) ;
return myConn;
}
常见的开源的数据库连接池:
DBCP(DataBase connection pool)数据库连接池是apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件
单独使用dbcp需要2个包:commons-dbcp.jar,commons-pool.jar
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,目前使用它的开源项目有Hibernate,Spring等
DBUtils
commons-dbutils是一个对JDBC进行简单封装的开源工具类库
可以配合数据连接池使用
DbUtils最主要的就两个,QueryRunner类和ResultSetHandler接口
QueryRunner类这个类主要用于提供各种重载形式的batch方法,query方法和update 方法
ResultSetHandler接口用于映射结果集
QueryRunner对象需要一个数据源对象
public class DBCPUtils {
private static DataSource dataSource;
static {
try {
InputStream is = DBCPUtils. class . getClassLoader ( ) . getResourceAsStream ( "db.properties" ) ;
Properties props = new Properties ( ) ;
props. load ( is) ;
dataSource = BasicDataSourceFactory. createDataSource ( props) ;
} catch ( Exception e) {
throw new RuntimeException ( e) ;
}
}
public static DataSource getDataSource ( ) {
return dataSource;
}
使用DBUtils操作SQL,解析结果集
查询单条记录
BeanHandler 映射单条记录对应的Bean的类型
public static void main ( String[ ] args) throws SQLException {
DataSource ds = DBCPUtils. getDataSource ( ) ;
QueryRunner qr = new QueryRunner ( ds) ;
String sql = "select * from student where id=?" ;
Student query = qr. query ( sql, new BeanHandler < Student> ( Student. class ) , 1 ) ;
System. out. println ( query) ;
}
查询所有记录
BeanListHandler 将SQL语句的结果映射封装为List集合
String sql = "select * from student " ;
List< Student> query = qr. query ( sql, new BeanListHandler < Student> ( Student. class ) ) ;
for ( Student student : query) {
System. out. println ( student) ;
}
插入语句
String sql = "insert into student values(null,?,?)" ;
int update = qr. update ( sql, "阿大" , "28" ) ;
修改语句
String sql = "update student set name = ? where id = ?" ;
int update = qr. update ( sql, "阿二" , 1 ) ;
删除语句
String sql = "delete from student where id = ?" ;
int update = qr. update ( sql, 2 ) ;
自定义处理器需要实现ResultSetHandler接口
public class MyHandler implements ResultSetHandler < List< User> > {
@Override
public List< User> handle ( ResultSet rs) throws SQLException {
List< student> list = new ArrayList < student> ( ) ;
while ( rs. next ( ) ) {
student stu = new student ( ) ;
stu. setId ( rs. getInt ( "id" ) ) ;
stu. setName ( rs. getString ( "name" ) ) ;
stu. setAge ( rs. getInt ( "age" ) ) ;
list. add ( stu) ;
}
return list;
}
}
调用自定义处理器
String sql = "select * from student" ;
List< student> list = qr. query ( sql, new MyHandler ( ) ) ;
for ( Student student : list) {
System. out. println ( student) ;
}