Java数据库连接
一、简介
JDBC即Java DataBase Connectivity,Java数据库连接。JDBC是一套标准,是Java与各大数据库厂商共同定制的一套接口,这套接口由各大数据库厂商进行了实现。
二、使用步骤
1.加载数据库驱动(JavaSE可以省略,JavaEE的web项目中不能省略)
mysql6以下驱动:com.mysql.jdbc.Driver
mysql6和6以上驱动:com.mysql.cj.jdbc.Driver
2.通过驱动管理器,获取JDBC的连接对象
连接地址格式:主协议:子协议://ip地址:端口号/数据库名称
mysql:jdbc:mysql://localhost:3306/test
oracle:jdbc:oracle:thin:@ip地址:1521/ORCL
3.通过连接对象,创建SQL执行对象(SQL运行环境)
4.通过SQL执行对象,执行SQL(执行查询操作返回值是true,否则是false,并不表示成功和失败)
5.释放连接
以下为Java连接MySQL数据库的例子:
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "");
Statement state = conn.createStatement();
state.execute("create table person(id int,nickname varchar(32))");
state.close();
conn.close();
三、常用类型和方法
1.DriverManage(驱动管理器):
获取数据库连接:
static Connection getConnection(String 数据库地址,String 账号 ,String 密码);
2.Connection(数据库连接对象):
创建SQL执行对象:
Statement createStatement();
3.Statement(SQL执行对象):
执行SQL语句(查询语句返回true,其它语句返回false):
boolean execute(String sql);
执行DQL语句(select):
ResultSet executeQuery(String sql);
执行DML语句(insert update delete)和DDL语句(create alter drop):
(返回int值,表示语句对数据库表格的影响行数,通常认为返回值>0表示执行成功)
int executeUpdate(String sql);
4.ResultSet(结果集对象):
指的是一个select语句的查询结果。
控制游标移动的常用方法
1.boolean next():
控制游标向下一行移动,移动成功返回true,下一行不存在移动失败,返回false。
2.boolean privious():
控制游标向上一行移动,移动成功返回true,上一行不存在移动失败,返回false。
3.boolean absolute(int 行号):
控制游标向指定行移动,移动成功返回true,行不存在移动失败,返回false。
4.boolean beforeFirst():
控制游标移动到第一行,移动成功返回true,没有第一行数据返回false。
5.boolean afterLast():
控制游标移动到最后一行,移动成功返回true,没有最后一行数据返回false。
获取游标指向行的字段值的常用方法
1.XXX getXXX(String 列名):
根据字段名,得到此字段的值。
2.XXX getXXX(int 字段的索引):
根据字段的索引,得到字段的值,索引从1开始。
public class Demo {
public static void insertData() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8", "root", "");
Statement state = conn.createStatement();
state.execute("insert into person values(1,'张三')");
state.close();
conn.close();
}
public static void insertPerson() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8", "root", "");
Statement state = conn.createStatement();
int flag = state.executeUpdate("insert into person values(1,'张三'),(2,'李四'),(3,'王五')");
state.close();
conn.close();
System.out.println("返回:"+flag);
}
public static void deletePerson() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8", "root", "");
Statement state = conn.createStatement();
int flag = state.executeUpdate("delete from person where id=1");
state.close();
conn.close();
System.out.println("返回:"+flag);
}
public static void updatePerson() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8", "root", "");
Statement state = conn.createStatement();
int flag = state.executeUpdate("update person set nickname='haha' where id=2");
state.close();
conn.close();
System.out.println("返回:"+flag);
}
public static void selectPerson() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8", "root", "");
Statement state = conn.createStatement();
ResultSet rs = state.executeQuery("select * from person");
while(rs.next()) {
int id = rs.getInt("id");
String nickname = rs.getString("nickname");
System.out.println("id="+id+"\tnickname="+nickname);
}
rs.close();
state.close();
conn.close();
}
}
四、静态工厂方法模式
1.简介:
工厂方法模式一种创建对象的模式。
工厂方法模式基于输入,应用在超类和多个子类之间的情况,这种模式将创建对象的责任转移到工厂类。
2.优点:
1.面向接口编程,体现了面向对象的思想。
2.降低了耦合,将创建对象的工作转移到了工厂类。
3.案例:
//水果接口
public interface Fruit {
void eat();
}
//苹果(水果的一种)
public class Apple implements Fruit{
@Override
public void eat() {
System.out.println("吃苹果");
}
}
//香蕉(水果的一种)
public class Banana implements Fruit{
@Override
public void eat() {
System.out.println("吃香蕉");
}
}
//静态工厂类
public class FruitFactory {
public static Fruit get() {
//return new Apple();
return new Banana();
}
}
//通过工厂获得对象
public class Demo {
public static void main(String[] args) {
Fruit f = FruitFactory.get();
f.eat();
}
}
五、DAO
DAO(Data Access Object)是一个数据访问接口,数据访问就是与数据库打交道,夹在业务逻辑与数据库资源中间。
为了建立一个健壮的Java应用,应该将所有对数据源的访问操作抽象封装在一个公共API中。用程序设计的语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口在逻辑上对应这个特定的数据存储。
DAO模式是标准的JavaEE设计模式之一,开发人员使用这个模式把底层的数据访问操作和上层的商务逻辑分开。一个典型的DAO实现有下列几个组件:
1.一个DAO工厂类;
2.一个DAO接口;
3.至少一个实现DAO接口的具体类;
4.数据传递对象(有些时候叫做Bean对象)。
六、预编译处理
1.SQL注入问题:
进行用户登录时,输入不存在的帐号和如下的密码:
1’ or ‘1’='1
结果显示登录成功。
这是因为用户输入的密码与我们的查询语句拼接后,使得我们的查询语句产生了歧义:
select * from user where username=‘帐号’ and password=‘密码’
拼接后:
select * from user where username=‘admin’ and password=‘1’ or ‘1’=‘1’
可以通过将SQL语句与参数分离,将参数作为SQL的特殊部分进行预处理来解决此问题。
2.PreparedStatement预编译:
实现原理
1.将未拼接参数的SQL语句,作为SQL指令,先传递给数据库进行编译;
2.再将参数传递给数据库,此时传递的参数不会再作为指令执行,只会被当作文本存在。
操作流程
1.得到PreparedStatement对象:
PreparedStatement state = conn.prepareStatement(“预编译的SQL语句”);
2.预编译的SQL语句(需要填充参数的位置,使用?代替即可):
select id from user where username=? and password=?;
3.填充参数:
state.setXXX(int index,XXX value);
setXXX中XXX指的是数据类型,index是SQL语句中?的索引值(从1开始),value是填充的参数值。
4.执行填充完毕参数的SQL:
boolean execute();
int executeUpdate();
ResultSet executeQuery();
性能问题
关于PreparedStatement与Statement谁的性能高,跟数据库是什么有关。
在mysql中,preparedStatement原理是拼接SQL,所以Statement性能高。
在Oracle中,preparedStatement原理是对SQL指令进行预处理,再传递的参数不具备特殊含义,有更好的SQL缓存策略,PreparedStatement高。
七、Java操作事务
JDBC事务通过连接对象开启、回滚、提交,只针对当前连接对象生效。
开启事务:conn.setAutoCommit(false);
回滚事务:conn.rollback();
提交事务:conn.commit();
关闭事务:conn.setAutoCommit(true);
八、批处理
将多条语句,放到一起批量处理。
批处理的原理:将多条SQL语句,转换为一个SQL指令,显著的提高大量SQL语句执行时的数据库性能。
1.Statement使用流程:
1.得到Statement对象:
Statement state = conn.createStatement();
2.将一条SQL语句加入到批处理中:
state.addBatch(String sql);
3.执行批处理:
state.executeBatch();
4.清空批处理:
state.clearBatch();
2.PreparedStatement使用流程:
1.得到PreparedStatement对象:
PreparedStatement state = conn.prepareStatement(“预编译的SQL”);
2.填充预编译的参数:
state.setXXX(1,填充参数);
3.将一条填充完毕参数的SQL加入到批处理中:
state.addBatch();
4.执行批处理:
state.executeBatch();
5.清空批处理:
state.clearBatch();
3.案例:
public class Main {
//statement
public static void insert1_1() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8","root","");
Statement state = conn.createStatement();
for (int i=0;i<100;i++) {
state.executeUpdate("insert into user values('admin','123"+i+"',50)");
}
state.close();
conn.close();
}
//statement批处理
public static void insert1_2() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8","root","");
Statement state = conn.createStatement();
for (int i=0;i<100;i++) {
//向批处理中加入一条SQL
state.addBatch("insert into user values('admin','123"+i+"',50)");
}
//执行批处理
state.executeBatch();
//清空批处理指令
state.clearBatch();
state.close();
conn.close();
}
//PreparedStatement
public static void insert2_1() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8","root","");
PreparedStatement state = conn.prepareStatement("insert into user values(?,?,?)");
for (int i=0;i<100;i++) {
state.setString(1,"admin");
state.setString(2,"123"+i);
state.setInt(3,50);
state.executeUpdate();
}
state.close();
conn.close();
}
//PreparedStatement批处理
public static void insert2_2() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8","root","");
PreparedStatement state = conn.prepareStatement("insert into user values(?,?,?)");
for (int i=0;i<100;i++) {
state.setString(1,"admin");
state.setString(2,"123"+i);
state.setInt(3,50);
state.addBatch();
}
//执行批处理
state.executeBatch();
//清空批处理指令
state.clearBatch();
state.close();
conn.close();
}
}
九、连接池(DataSource)
1.简介:
连接池用于缓存连接,当我们需要使用连接时,可以不用再创建连接,而是直接从连接池中获取连接。当连接池中存在空闲连接时,会将空闲连接给到程序使用;当连接池中不存在空闲连接时,且连接池未满时,则创建连接提供给程序使用,并在程序使用完毕后缓存连接;当连接池中不存在空闲连接时,且连接池已满时,则排队等候空闲连接的出现。
2.注意:
使用连接池中的连接对象操作数据库时,操作完毕依然需要释放连接(调用close());
连接池中的连接在设计时,使用了动态代理设计模式加装饰者设计模式,当调用close方法时,代理没有关闭这个连接,而是将连接重新放入了池中。
3.DBCP连接池:
1.引入jar文件
2.将配置文件properties文件放到src文件夹下
3.将properties文件转换为Properties对象
Properties ppt = new Properties();
ppt.load(配置文件的输入流);
4.通过连接池的工厂类,创建连接池
DataSource ds = BasicDataSourceFactory.createDataSource(ppt);
5.从池中获取一个连接
Connection conn = ds.getConnection();
public class Demo {
public static void main(String[] args) throws Exception {
Properties ppt = new Properties();
InputStream is = Demo.class.getClassLoader().getResourceAsStream("dbcp.properties");
ppt.load(is);
DataSource dataSource = BasicDataSourceFactory.createDataSource(ppt);
Connection conn = dataSource.getConnection();
Statement state = conn.createStatement();
state.execute("insert into user values('zhangsan','lisi',50)");
state.close();
conn.close();
}
}
DBCP连接池的工具类:
public class DBCPUtil {
private static DataSource ds;
static {
Properties ppt = new Properties();
try {
ppt.load(DruidUtil.class.getClassLoader().getResourceAsStream("dbcp.properties"));
ds = BasicDataSourceFactory.createDataSource(ppt);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 从连接池中取出一个连接给用户
* @return
*/
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
public static void close(Connection conn, Statement state, ResultSet rs) {
try {
rs.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
state.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
conn.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
}
}
4.德鲁伊连接池:
1.引入jar文件
2.将配置文件properties文件放到src文件夹下
3.将properties文件转换为Properties对象
Properties ppt = new Properties();
ppt.load(配置文件的输入流);
4.通过连接池的工厂类,创建连接池
DataSource ds = DruidDataSourceFactory.createDataSource(ppt);
5.从池中获取一个连接
Connection conn = ds.getConnection();
public class Demo {
//Druid
public static void main(String[] args) throws Exception {
Properties ppt = new Properties();
InputStream is = Demo.class.getClassLoader().getResourceAsStream("druid.properties");
ppt.load(is);
DataSource dataSource = DruidDataSourceFactory.createDataSource(ppt);
Connection conn = dataSource.getConnection();
Statement state = conn.createStatement();
state.execute("insert into xzk_user values('zhangsan','lisi',50)");
state.close();
conn.close();
}
}
Druid连接池的工具类:
public class DruidUtil {
private static DataSource ds;
static {
Properties ppt = new Properties();
try {
ppt.load(DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(ppt);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 从连接池中取出一个连接给用户
* @return
*/
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
public static void close(Connection conn, Statement state, ResultSet rs) {
try {
rs.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
state.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
try {
conn.close();
} catch (Exception throwables) {
throwables.printStackTrace();
}
}
}