上面说到JDBC是一组接口组成的,下面我们就必须知道JDBC的核心API,也就是常用是哪些接口与类:
下面大致说一下执行流程:
然后我们看一个普通实例:
在写这个实例之前,我来说两点:
第一点:JDBC这一系列接口都是由相应厂商实现的,也就是说,我们想要用到这些已经做好的接口,就必须去导入相应厂商的jar包,这里我用的JDBC jar包是:
注意右键add as library才能作为库让我们调用。
第二点:关于注册数据库驱动的问题,我们说了Driver这个类是我们必须要用到的,他可以帮助我我们注册驱动,源代码如下:
你看内部写了一个静态代码块,调用了驱动管理类的方法给我们注册驱动。
换句话说,我们可以这样来写:
Class.forname("com.mysql.jdbc.Driver");利用反射加载一个类对象,它的目的是加载这个类之后,调用静态代码块加载驱动。
JdbcDemo1.java:
package jdbc;
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcDemo1 {
public static void main(String[] args) {
Connection conn = null;
Statement state = null;
try {
//第一步注册驱动
Driver driver = new Driver();
//需要一个Properties来提供用户名与密码属性
Properties properties = new Properties();
properties.setProperty("user","root");//user固定的属性
properties.setProperty("password","5201314");//password固定的属性
//第二步得到连接对象
conn = driver.connect("jdbc:mysql:///pxx",properties);//这个可以获取一个连接对象
//然后创建一个执行的sql语句
String sql = "update st5 set age = 520 where id = 1";
state = conn.createStatement();
int flag = state.executeUpdate(sql);
if(flag == 1) {
System.out.println("程序执行成功");
} else {
System.out.println("程序执行失败");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//注意资源的关闭
if(state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
上面就是我更改了我数据库表st5的年龄:
原来表的数据:
现在表的数据:
我们还可以有另外一种写法:
JdbcDemo2.java:
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcDemo2 {
public static void main(String[] args) {
Connection conn = null;
Statement state = null;
try {
conn = DriverManager.getConnection("jdbc:mysql:///pxx","root","5201314");
//然后创建一个执行的sql语句
String sql = "update st5 set age = 360 where id = 1";
state = conn.createStatement();
int flag = state.executeUpdate(sql);
if(flag == 1) {
System.out.println("程序执行成功");
} else {
System.out.println("程序执行失败");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
上面也会执行成功,那么这个时候有个疑问,驱动在哪注册的?
我之前说了,DriverManager可以帮我们注册驱动:
先看一下它调用的连接函数:
内部要去调用了一个getConnection()函数,再次进入,看到下面这段代码:
他会用内部一个Driver对象去调用connect方法,这个时候,Driver会加载到内存,自动注册驱动。
下面说一下Statement常用的两种查询语句:
我们可以看到executeQuery(String sql) => 返回一个结果集ResultSet
这个ResultSet里面有一个next()方法:
下面再来说一下如何获取表中的各个字段信息:
下面看一个类封装信息,任务就是把一个数据库中一张表每一条记录封装成一个对象,然后在存到集合里面。
先看我们的需要封装的表:
先来创建一个Emp类:
Emp.java
package domain;
import java.util.Date;
public class Emp {
int id;
String name;
String gender;
double salary;
Date joinDate;
int deptId;
public Emp() {
}
public Emp(int id, String name, String gender, double salary, Date joinDate, int deptId) {
this.id = id;
this.name = name;
this.gender = gender;
this.salary = salary;
this.joinDate = joinDate;
this.deptId = deptId;
}
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Date getJoinDate() {
return joinDate;
}
public void setJoinDate(Date joinDate) {
this.joinDate = joinDate;
}
public int getDeptId() {
return deptId;
}
public void setDeptId(int deptId) {
this.deptId = deptId;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
", gender=" + gender +
", salary=" + salary +
", joinDate=" + joinDate +
", deptId=" + deptId +
'}';
}
}
再来看一个数据库操作类:
JdbcDemo3.java:
package jdbc;
import domain.Emp;
import java.sql.*;
import java.util.ArrayList;
public class JdbcDemo3 {
public static void main(String[] args) {
Connection conn = null;
Statement state = null;
ArrayList<Emp> list = new ArrayList<Emp>();
ResultSet res = null;
try {
conn = DriverManager.getConnection("jdbc:mysql:///pxx","root","5201314");
//创建语句对象
state = conn.createStatement();
//执行sql
res = state.executeQuery("select * from emp");
Emp emp = null;
while(res.next()) {
int id = res.getInt("id");
String name = res.getString("name");
String gender = res.getString("gender");
double salary = res.getDouble("salary");
Date joinDate = res.getDate(5);
int deptId = res.getInt(6);
emp = new Emp();
emp.setId(id);
emp.setName(name);
emp.setGender(gender);
emp.setSalary(salary);
emp.setJoinDate(joinDate);
emp.setDeptId(deptId);
list.add(emp);
}
//上面就把每条数据放到了集合中
System.out.println(list);//直接输出一下集合
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(res != null) {
try {
res.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
下面看一下运行结果:
上面的代码我们会看到比较浪费效率的一件事儿,就是每次我们链接数据库,我们都要获得输入链接URL,输入用户名,输入密码,获得连接对象,获得语句对象,那么有没有一种方法可以把这些常用的方法给封装起来,下次我们直接调用就可以了,不用重复写了。
那么这个时候,就要写一个数据库工具类:
把连接需要的属性放到配置文件中。把获取连接对象方法封装起来,把关闭资源封装起来。这两个类型的操作每次写是最费劲的。
先来看我们的配置文件,放在src下面:(最好是放在src下面),名字以properties结尾:
utils.properties:
然后来看我的JdbcUtils.java:
package utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String url;
private static String user;
private static String password;
//我们要从配置文件中加载信息,不可能就是我们不写连接程序,还要写找配置文件程序吧,直接静态代码
//加载就处理
static{
Properties properties = new Properties();
InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("utils.properties");
try {
properties.load(is);
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,user,password);
}
public static void close(Connection conn, Statement state) {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//假如有资源结果集要处理,需要关闭资源
public static void close(Connection conn, Statement state, ResultSet res) {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(res != null) {
try {
res.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
然后看我们测试文件JdbcDemo4.java:
package jdbc;
import utils.JdbcUtils;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcDemo4 {
public static void main(String[] args) {
//得到连接对象
Connection conn = null;
Statement stmt = null;
try {
conn = JdbcUtils.getConnection();
stmt = conn.createStatement();
String sql = "update st5 set age = 321 where id = 1";
int count = stmt.executeUpdate(sql);
if(count > 0) {
System.out.println("数据修改成功");
} else {
System.out.println("数据修改失败");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(conn,stmt);
}
}
}
查一下数据库,明显修改成功:
下面模拟一下从数据库查找账号与密码,然后登录服务器的一个过程:
这是我们从数据库找的一张表:
再次之前,我来说一说next()方法与nextline()方法的区别对吗
看一下模拟用户登录代码:
JdbcDemo4.java
package jdbc;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.Scanner;
public class JdbcDemo4 {
public static void main(String[] args) {
//得到连接对象
Connection conn = null;
Statement stmt = null;
ResultSet res = null;
try {
conn = JdbcUtils.getConnection();
stmt = conn.createStatement();
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String user = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//下面是一条错误的sql语句,是因为没有把输入当成字符串处理
//String sql = "select * from user where username = " + user + "and password = " + password;
String sql = "select * from user where username = '" + user + "' and password = '" + password + "'";
System.out.println(sql);
res = stmt.executeQuery(sql);
if(res.next()) {
System.out.println("登录成功");
} else {
System.out.println("登录失败");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(conn,stmt,res);
}
}
}
上面只要用户名密码输入正确了,就会提示我们登录成功
但是,会出现一个问题就是sql注入,那么啥是sql注入问题:
就是通过欺骗mysql来构成一种合理的sql语句,比如下面:
看看是不是欺骗了MySQL,从而拿到了全部的用户信息。
那么通过我们的客户端,我们可以这样来写:
写sql注入语句的时候,要注意的是,符合语法要求。
那么这个时候,我们就要引入一个特别安全的sql语句对象来执行sql
上面 PreparedStatement接口继承了Statement接口,也就是说数据库厂商帮我们实现了上面接口的所有方法。
下面说一下PreparedStatement执行原理:
然后说一下使用方法:
直接上源代码:
JdbcDemo5.java
package jdbc;
import java.sql.*;
import java.util.Scanner;
public class JdbcDemo5 {
public static void main(String[] args) {
//得到连接对象
Connection conn = null;
PreparedStatement stmt = null;
ResultSet res = null;
try {
conn = JdbcUtils.getConnection();
// stmt = conn.createStatement();
//下面是一条错误的sql语句,是因为没有把输入当成字符串处理
String sql = "select * from user where username = ? and password = ?";
//注意得到PreparedStatement对象
stmt = conn.prepareStatement(sql);
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String user = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//通过占位符来控制参数
stmt.setString(1,user);
stmt.setString(2,password);//传入的就是一个字符串,你不可能在拼接or
if(res.next()) {
System.out.println("登录成功");
} else {
System.out.println("登录失败");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(conn,stmt,res);
}
}
}
c3po数据库连接池
数据库连接池解决的问题
频繁的创建数据库链接,消耗内存
下面是他需要的两个jar和一个配置文件
把配置文件拿过来看一下
c3p0-config.xml
<c3p0-config>
<!--使用默认的配置读取连接池
走了一个空参构造函数的配置
-->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test</property>
<property name="user">root</property>
<property name="password">5201314</property>
<!--连接池参数-->
<property name="initialPoolSize">5</property>
<!--最大的连接数量-->
<property name="maxPoolSize">10</property>
<!--超时时间-->
<property name="checkoutTimeout">3000</property>
</default-config>
<!--
在构造ComPooledDataSource的时候,构造函数
传入一个参数名字走指定配置项
-->
<name-config name="otherc3p0">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test</property>
<property name="user">root</property>
<property name="password">5201314</property>
<!--连接池参数-->
<property name="initialPoolSize">5</property>
<!--最大的连接数量-->
<property name="maxPoolSize">10</property>
<!--超时时间-->
<property name="checkoutTimeout">3000</property>
</name-config>
</c3p0-config>
这里说明两点
第一点:配置文件名字是不能改动的,只能叫c3p0-config.xml这个名字
第二点:配置里面的属性名字不能改动。
下面说一下关于连接数量问题:
下面说一下druid连接池
这个连接池与c3p0有一些不同的点就是,它的配置文件必须是properties,但是名字可以随便起,可以放到任何类目录下面
下面直接来看一下配置文件
它的做法就是通过工厂获取一个连接池对象
DataSource ds = DruidSourceFatory.createDataSource(pro);
它的配置信息如下