JDBC:Java DataBase Connectivity,java动态数据库连接技术是一种用于执行SQL语句的Java API。JDBC可以有多套实现类,例如:Mysql、Oracle、SqlServer等数据库。JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。例如mysql-connector-java-5.1.6.jar
JDBC与数据库驱动的关系本质上就是接口与实现的关系。
Sun公司提供了供程序员调用的类和接口,集成在了java.sql和javax.sql包里,例如:
以eclipsed的Java项目和mysql-connector-java-5.1.6.jar驱动为例,将mysql-connector-java-5.1.6.jar添加到Libraries里。
具体做法:
案例:查询jdbcdb数据库中person表的全部数据
package com.gaj.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 用java代码连接数据库测试
* JDBC技术
* @author Jan
*
*/
public class JDBCTest {
public static void main(String[] args) {
// 定义mysql的驱动类 用于反射
String driver = "com.mysql.jdbc.Driver";
// 定义mysql数据库的url
String url = "jdbc:mysql://127.0.0.1:3306/jdbcdb?characterEncoding=UTF-8";
// 定义连接数据库的用户名
String user = "root";
// 定义连接数据库的密码
String password = "root";
// 定义sql语句
String sql = "select * from person";
// 声明
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
// 1.加载JDBC驱动类
// 方法一:注册驱动
// DriverManager.registerDriver(new Driver());
// 方法二:反射
// Driver d = (Driver) Class.forName(driver).newInstance();
// DriverManager.registerDriver(d);
// 方法三:反射简写(JDK1.6之后可以省略)
// 程序运行期间DriverManager会根据驱动类型自动匹配
Class.forName(driver);
// 2.获取连接数据库的对象 与数据库建立连接
conn = DriverManager.getConnection(url, user, password);
// 3.创建操作数据库的对象
stat = conn.createStatement();
// 4.执行查询得到数据结果集
rs = stat.executeQuery(sql);
// 5.解析结果集 使用游标判断是否还有数据
System.out.println("id\tname\tsex\tage\tfrom");
while(rs.next()){
// int getInt(int columnIndex) 按列的序号获取数据,从1开始
System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getInt(4) + "\t" + rs.getString(5));
// int getInt(String columnLabel) 按列名获取数据
// System.out.println(rs.getInt("id") + "\t" + rs.getString("name") + "\t" + rs.getString("sex") + "\t" + rs.getInt("age") + "\t" + rs.getString("from"));
// int -> String 自动转换
// System.out.println(rs.getString("id") + "\t" + rs.getString("name") + "\t" + rs.getString("sex") + "\t" + rs.getString("age") + "\t" + rs.getString("from"));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭资源
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stat != null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
Class.forName("com.mysql.jdbc.Driver"):
JDK1.6之后DriverManger借助ServiceLoader会在程序运行期间加载导入的mysqljar包然后找到驱动自动注册,不需要再手工注册,也就是说只要你导入JAR包其实不用指定驱动DriverManager也能找到某个类型的驱动自己进行设置。
ServiceLoader与ClassLoader是Java中2个即相互区别又相互联系的加载器。JVM利用ClassLoader将类载入内存,这是一个类生命周期的第一步(一个java类的完整的生命周期会经历加载、连接、初始化、使用、和卸载五个阶段,当然也有在加载或者连接之后没有被初始化就直接被使用的情况)。
ServiceLoader是一个简单的服务提供者加载设施。服务是一个熟知的接口和类(通常为抽象类)集合。服务提供者是服务的特定实现。提供者中的类通常实现接口,并子类化在服务本身中定义的子类。服务提供者可以以扩展的形式安装在Java平台的实现中,也就是将jar文件放入任意常用的扩展目录中。也可通过将提供者加入应用程序类路径,或者通过其他某些特定于平台的方式使其可用。唯一强制要求的是,提供者类必须具有不带参数的构造方法,以便它们可以在加载中被实例化。
public static Connection getConnection(String url, String user, String password)
jdbc:mysql://127.0.0.1:3306/jdbcdb?characterEncoding=UTF-8
,本地连接还可以简写为jdbc:mysql:///jdbcdb
Statement createStatement();
Statement stmt = conn.createStatement();
方法名 | 说明 |
---|---|
ResultSet executeQuery(String sql) | 执行SQL查询并获取到ResultSet对象 |
int executeUpdate(String sql) | 执行插入、删除、更新等操作,返回值是执行sql所影响的行数 |
boolean execute(String sql) | 执行任意sql语句,获得一个布尔值,表示是否返回ResultSet |
方法名 | 说明 |
---|---|
boolean next() | 将游标从当前位置向下移动一行 |
boolean previous() | 将游标从当前位置向上移动一行 |
void clsoe() | 关闭ResultSet对象 |
int getInt(int colIndex) | 以int形式获取结果集当前行指定列号值 |
int getInt(String colLabel) | 以int形式获取结果集当前行指定列名值 |
float getFloat(int colIndex) | 以float形式获取结果集当前行指定列号值 |
float getFloat(String colLabel) | 以float形式获取结果集当前行指定列名值 |
String getString(int colIndex) | 以String形式获取结果集当前行指定列号值 |
String getString(String colLabel) | 以String形式获取结果集当前行指定列名值 |
// 添加数据
@Test
public void insertTest() throws Exception{
// 定义驱动类
String driver = "com.mysql.jdbc.Driver";
// 定义URL
String url = "jdbc:mysql://127.0.0.1:3306/jdbcdb?characterEncoding=utf-8";
// 定义user
String user = "root";
// 定义password
String password = "root";
// 定义sql
String sql = "insert into person(`id`, `name`, `sex`, `age`, `from`) values(null, '王小花', '女', 18, '湖南省');";
// 1.加载驱动类
Class.forName(driver);
// 2.获取数据库连接
Connection conn = DriverManager.getConnection(url, user, password);
// 3.创建操作数据库对象
Statement stat = conn.createStatement();
// 4.执行结果 int executeUpdate(String sql) 返回int值,代表影响行数
int count = stat.executeUpdate(sql);
// 5.判断结果 影响行数如果大于0说明执行成功
System.out.println((count > 0) ? "执行成功!" : "执行失败!");
// 6.关闭连接
stat.close();
conn.close();
}
// 删除数据
@Test
public void deleteTest() throws Exception{
// 定义驱动类
String driver = "com.mysql.jdbc.Driver";
// 定义URL
String url = "jdbc:mysql://127.0.0.1:3306/jdbcdb?characterEncoding=utf-8";
// 定义user
String user = "root";
// 定义password
String password = "root";
// 定义sql
String sql = "delete from person where id = 3";
// 1.加载驱动类
Class.forName(driver);
// 2.获取数据库连接
Connection conn = DriverManager.getConnection(url, user, password);
// 3.创建数据库操作对象
Statement stat = conn.createStatement();
// 4.操作数据
int count = stat.executeUpdate(sql);
// 5.判断结果
System.out.println((count > 0) ? "执行成功!" : "执行失败!");
// 6.关闭连接
stat.close();
conn.close();
}
// 修改数据
@Test
public void updateTest() throws Exception{
// 定义Driver类
String driver = "com.mysql.jdbc.Driver";
// 定义URL
String url = "jdbc:mysql://127.0.0.1:3306/jdbcdb?characterEncoding=utf-8";
// 定义user
String user = "root";
// 定义password
String password = "root";
// 定义sql
String sql = "update person set `name`='李建军', `age`=28 where `id` = 4;";
// 1.加载驱动类
Class.forName(driver);
// 2. 获取连接数据库对象
Connection conn = DriverManager.getConnection(url, user, password);
// 3. 创建数据库操作对象
Statement stat = conn.createStatement();
// 4. 执行sql
int count = stat.executeUpdate(sql);
// 5. 判断结果
System.out.println((count > 0) ? "执行成功!" : "执行失败!");
// 6. 关闭数据库连接
stat.close();
conn.close();
}
// 查询数据
@Test
public void queryAllTest() throws Exception{
// 定义驱动类
String driver = "com.mysql.jdbc.Driver";
// 定义URL 简写 默认连接本机数据库 字符集默认utf-8
String url = "jdbc:mysql:///jdbcdb";
// 定义user
String user = "root";
// 定义password
String password = "root";
// 定义sql
String sql = "select * from person";
// 1.加载驱动类
Class.forName(driver);
// 2.获取连接数据库对象
Connection conn = DriverManager.getConnection(url, user, password);
// 3.创建数据库操作对象
Statement stat = conn.createStatement();
// 4.执行sql
ResultSet rs = stat.executeQuery(sql);
// 5.解析结果集
System.out.println("id\tname\tsex\tage\tfrom");
while(rs.next()){
System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getInt(4) + "\t" + rs.getString(5));
}
// 6.关闭数据库资源
rs.close();
stat.close();
conn.close();
}
package com.gaj.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCUtil {
// 定义mysql数据库驱动需要加载的类
private static final String CONN_DRIVER = "com.mysql.jdbc.Driver";
// 定义需要连接数据库的URL
private static final String CONN_URL = "jdbc:mysql://127.0.0.1:3306/jdbcdb?charaterEncoding=utf8";
// 定义连接数据库的用户名
private static final String CONN_USER = "root";
// 定义连接数据库的密码
private static final String CONN_PASS = "root";
/**
* 获取数据连接对象
* @return 返回一个数据库连接对象
*/
public static Connection getConnection(){
Connection conn = null;
try {
// 加载数据库驱动
Class.forName(CONN_DRIVER);
// 获取数据库连接
conn = DriverManager.getConnection(CONN_URL, CONN_USER, CONN_PASS);
} catch (ClassNotFoundException e) {
System.out.println("没有找到驱动类:" + e.getMessage());
e.printStackTrace();
} catch (SQLException e) {
System.out.println("数据库连接失败:" + e.getMessage());
e.printStackTrace();
}
return conn;
}
/**
* 释放资源
* 关个连接也能报错???
* 关闭所有数据库连接,如果没有创建该对象直接填null就好了
* @param conn 数据库连接对象
* @param stat 数据库操作对象
* @param rs 数据结果集对象
*/
public static void closeAllConnection(Connection conn, Statement stat, ResultSet rs){
// 先开后关原则
if(null != rs){
try {
rs.close();
} catch (SQLException e) {
System.out.println("关闭数据结果集出错:" + e.getMessage());
e.printStackTrace();
}
}
if(null != stat){
try {
stat.close();
} catch (SQLException e) {
System.out.println("关闭数据库操作对象出错:" + e.getMessage());
e.printStackTrace();
}
}
if(null != conn){
try {
conn.close();
} catch (SQLException e) {
System.out.println("关闭数据库连接出错:" + e.getMessage());
e.printStackTrace();
}
}
}
}
SQL注入:用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义。
// 根据姓名查询人员的数据
public void findPersonByName(String name) throws Exception{
Connection conn = JDBCUtil.getConnection();
String sql = "select * from person where `name` = " + name;
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getString(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getString(4) + "\t" + rs.getString(5));
}
JDBCUtil.closeAllConnection(conn, stat, rs);
}
// 测试findPersonById()
@Test
public void findPersonByIdTest() throws Exception{
// 正常查询
findPersonByName("'张三'");
}
结果:只有一条数据信息
// 测试findPersonById()
@Test
public void findPersonByIdTest() throws Exception{
// 注入问题存在
findPersonByName("'张三' or true");
}
结果:本来只想查询一条数据,但由于某某修改参数将所有数据都查询出来了,将原有的意义给改了。
原因:statement对象只能机械的执行SQL语句,其他的一律不管
java.sql.preparedStatement
继承自java.sql.Statement
,预编译对象,是Statement对象的子类。 // 根据姓名查询人员的数据
public void findPersonByName(String name) throws Exception{
Connection conn = JDBCUtil.getConnection();
// sql语句 占位符占位
String sql = "select * from person where `name` = ?";
// 将sql传入预编译prepareStatement()方法 进行预编译
PreparedStatement ps = conn.prepareStatement(sql);
// 给占位符赋值
ps.setString(1, name);
// 执行获得结果集 这里由于之前传过参了,因此不用再传
ResultSet rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getString(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getString(4) + "\t" + rs.getString(5));
}
JDBCUtil.closeAllConnection(conn, ps, rs);
}
// 测试findPersonByName()
@Test
public void findPersonByNameTest() throws Exception{
// 正常查询 正常结果
// findPersonByName("张三");
// 过滤了关键字,查询不到数据了,避免了注入
findPersonByName("张三 or true");
}
改进版新增如下几点:
package com.gaj.entity;
/**
* JDBC需要操作的人员实体类
* 属性需要与数据库的字段类型相匹配
* 属性名称可以不匹配 在不修改数据库字段的前提下 可以使用sql语句的as重命名 来保证字段名一致
* @author Jan
*
*/
public class Person {
private Integer pid;
private String pname;
private String sex;
private Integer age;
private String from;
public Integer getPid() {
return pid;
}
public void setPid(Integer pid) {
this.pid = pid;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
@Override
public String toString() {
return "Person [pid=" + pid + ", pname=" + pname + ", sex=" + sex + ", age=" + age + ", from=" + from + "]";
}
}
package com.gaj.function;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import com.gaj.entity.Person;
import com.gaj.utils.JDBCUtil;
/**
* 改进版
* JDBC连接数据库的CURD操作
* 1.操作实体类对象 OOP思想的体现
* 2.封装功能函数
* 3.使用自己封装的DBUtil类简化获取连接和关闭资源
* 4.PreparedStatement 注解问题的解决
*
* @author Jan
* @version 2.0
*
*/
public class JDBCFunction {
// 新增操作
public static int insert(Person person) throws Exception {
// 1.使用自己封装的JDBCUtil加载驱动类并创建数据库连接对象
Connection conn = JDBCUtil.getConnection();
// 2.定义sql语句 使用占位符占位
String sql = "insert into person values(null,?,?,?,?);";
// 3.创建数据库预编译操作对象 传入sql语句进行预编译
PreparedStatement ps = conn.prepareStatement(sql);
// 4.占位符赋值
// void setString(int parameterIndex, String x) 占位符的位置,设置的值
ps.setString(1, person.getPname());
ps.setString(2, person.getSex());
ps.setInt(3, person.getAge());
ps.setString(4, person.getFrom());
// 5.执行sql语句 之前预编译过了 因此这里不用传参
int count = ps.executeUpdate();
// 6.关闭数据库连接
JDBCUtil.closeAllConnection(conn, ps, null);
// 7.返回影响行数
return count;
}
// 删除操作
public static int delete(Person person) throws Exception{
// 获取连接
Connection conn = JDBCUtil.getConnection();
// sql 占位符
String sql = "delete from person where id = ?";
// 创建预编译执行对象
PreparedStatement ps = conn.prepareStatement(sql);
// 占位符赋值
ps.setInt(1, person.getPid());
// 执行sql
int count = ps.executeUpdate();
// 释放资源
JDBCUtil.closeAllConnection(conn, ps, null);
// 返回影响行数
return count;
}
// 修改操作
public static int update(Person person) throws Exception{
// 获取连接
Connection conn = JDBCUtil.getConnection();
// 编写sql
String sql = "update person set name =?, sex=?,age=?,`from`=? where id=?";
// 预编译
PreparedStatement ps = conn.prepareStatement(sql);
// 占位符赋值
ps.setString(1, person.getPname());
ps.setString(2, person.getSex());
ps.setInt(3, person.getAge());
ps.setString(4, person.getFrom());
ps.setInt(5, person.getPid());
// 执行sql
int count = ps.executeUpdate();
// 关闭资源
JDBCUtil.closeAllConnection(conn, ps, null);
// 返回影响行数
return count;
}
// 查询操作
public static List<Person> queryAll() throws Exception{
// 获取连接
Connection conn = JDBCUtil.getConnection();
// 编写sql语句
String sql = "select * from person";
// 创建执行数据库执行对象
PreparedStatement ps = conn.prepareStatement(sql);
// 执行sql语句
ResultSet rs = ps.executeQuery();
// 解析结果集
List<Person> list = new ArrayList<>();
Person person = null;
while(rs.next()){
person = new Person();
person.setPid(rs.getInt("id"));
person.setPname(rs.getString("name"));
person.setSex(rs.getString("sex"));
person.setAge(rs.getInt("age"));
person.setFrom(rs.getString("from"));
// 添加到list集合
list.add(person);
}
// 关闭连接
JDBCUtil.closeAllConnection(conn, ps, rs);
// 返回person数组
return list;
}
/**
* 按id查找该对象是否存在,存在返回该对象,不存在返回null
* @param id
* @return 返回Person类的一个对象
* @throws Exception
*/
public static Person findPeresonById(Integer id) throws Exception{
// 定义person对象
Person person = null;
// 获取数据库连接
Connection conn = JDBCUtil.getConnection();
// 编写sql
String sql = "select * from Person where id = ?";
// 预编译
PreparedStatement ps = conn.prepareStatement(sql);
// 占位符
ps.setInt(1, id);
// 执行
ResultSet rs = ps.executeQuery();
// 解析一行
if(rs.next()){
// 创建person对象
person = new Person();
// 给person类赋值
person.setPid(rs.getInt("id"));
person.setPname(rs.getString("name"));
person.setSex(rs.getString("sex"));
person.setAge(rs.getInt("age"));
person.setFrom(rs.getString("from"));
}
// 关闭资源
JDBCUtil.closeAllConnection(conn, ps, rs);
// 返回person类
return person;
}
// 根据姓名查询人员的数据
public static void findPersonByName(String name) throws Exception{
Connection conn = JDBCUtil.getConnection();
String sql = "select * from person where `name` = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, name);
ResultSet rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getString(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getString(4) + "\t" + rs.getString(5));
}
JDBCUtil.closeAllConnection(conn, ps, rs);
}
}
package com.gaj.test;
import java.util.List;
import org.junit.Test;
import com.gaj.entity.Person;
import com.gaj.function.JDBCFunction;
/**
* 测试类 测试JDBCFunction的CURD操作
* @author Jan
*
*/
public class Test2 {
/**
* 测试按Id查找
* @throws Exception
*/
@Test
public void findPersonByIdTest() throws Exception{
Person person = JDBCFunction.findPeresonById(11);
System.out.println(person);
}
/**
* 测试新增人员信息
* @throws Exception
*/
@Test
public void insertTest() throws Exception {
// 创建Person对象
Person person = new Person();
// 给Person对象赋值
person.setPname("张三");
person.setSex("男");
person.setAge(22);
person.setFrom("江西省");
// 执行新增操作
int count = JDBCFunction.insert(person);
// 判断执行结果
System.out.println(count > 0 ? "Insert Successed!" : "Insert Failed!");
}
/**
* 测试删除人员信息
* @throws Exception
*/
@Test
public void deleteTest() throws Exception{
Person person = JDBCFunction.findPeresonById(7);
if(person != null){
int count = JDBCFunction.delete(person);
System.out.println(count > 0 ? "Delete Successed!" : "Delete Failed!");
}else{
System.out.println("The object not exists!");
}
}
/**
* 测试修改人员信息
* @throws Exception
*/
@Test
public void updateTest() throws Exception{
Person person = JDBCFunction.findPeresonById(6);
if(person != null){
person.setPname("李四");
person.setAge(20);
person.setSex("男");
person.setFrom("浙江省");
int count = JDBCFunction.update(person);
System.out.println(count > 0 ? "Update Successed!" : "Update Failed!");
}else{
System.out.println("The object not exists!");
}
}
/**
* 测试查询全部
* @throws Exception
*/
@Test
public void QueryAllTest() throws Exception{
List<Person> list = JDBCFunction.queryAll();
for (Person person : list) {
System.out.println(person);
}
}
/**
* 按名字查询一条数据
* @throws Exception
*/
@Test
public void findPersonByNameTest() throws Exception{
// 正常查询
// JDBCFunction.findPersonByName("张三");
// 查询不到数据
JDBCFunction.findPersonByName("张三 or true");
}
}