JDBC数据库连接技术
为什么使用JDBC?
因为在Java使用的数据都是瞬时数据,在内存中运行完后会被销毁,数据库可以持久化存储数据,但两者之间是独立的个体,需要将两个独立个体连接起来,这时候 就需要使用JDBC数据库连接技术
JDBC概念
JDBC(Java DataBase Connectivity) 是Java提供的一套用于连接访问数据库执行SQL的标准规范(接口),接口的实现由对应的数据库厂商提供
JDBC执行流程
历史: 以前在JDBC连接技术中,数据库连接的实现由程序员编写OJDBC技术,但是这种技术在切换数据库后需要重新编写实现
现在: 为了节约开发和时间从成本,数据库的实现由数据库厂商统一提供-----JDBC技术
实现步骤:
1.加载驱动 Class.forName(path)
2.通过DriverManager.getConntion(url,name,password)获取连接,并返回连接对象
3.通过Connection去接收连接对象
4.通过conn对象创建出Statement执行接口对象
5.statement对象执行sql,进行操作
6.如果是更新方法返回受影响行数(int),执行查询方法返回Resultset结果集对象
7.获取数据
8.关闭资源
JDBC中核心的接口和类
1,DriverManager类:这是一个类
1,负责管理驱动,注册驱动
2,通过该类中的静态方法,getConnection()方法就可以得到一个该数据库的连接对象
2,Connection接口
1,负责跟数据库建立了一条连接
2,通过该接口的实现类对象,还可以得到一个Statement对象
3,Statement接口
1,相当于小货车,负责执行sql语句,并且返回结果
2,通过调用该接口中的查询的方法,就返回了一个ResultSet结果集对象
4,ResultSet接口
1,当查询的时候,把数据库中的数据封装在结果集当中,输回给java
2,遍历该结果集中的数据,通过一个next(),指针,每次读取一行记录
连接数据库
ublic class ConnectionDemo {
public static void main(String[] args) {
Connection conn = null;
try {
//1.加载驱动,可能找不到这个名称的类,所以报出找不到类的异常
Class.forName("com.mysql.jdbc.Driver");
//2.通过DriverManager建立连接,可能出现url错误连接不上报出异常
/*
* jdbc: 主协议----使用什么技术
* mysql: 自协议---连接的数据库
* :// 连接协议
* localhost 连接IP 表示本机与127.0.0.1相同
* 3306 数据库指定端口号
* test 连接的数据库
* ? 表示拼接属性
* characterEncoding 编码字符集设置 utf-8
* root 用户名和密码 如果没有密码不填写第三个参数
*/
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8","root","root");
System.out.println(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用statement对象添加数据
public class SaveGrade {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
Grade grade = new Grade(5, "研究生", "没事就研究研究");
try {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.建立连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8", "root", "root");
//3.获取到statement执行对象
st = conn.createStatement();
//4.准备需要执行的SQL语句
//statement采用的是拼接式sql执行,字符型数据需要主动加上单引号
String sql = "insert into grade values("+grade.getGid()+",'"+grade.getGname()+"','"+grade.getGdesc()+"')";
//5.执行sql并返回受影响行数,用于执行更新(增删改)调用executeUpdate(sql)方法
int row = st.executeUpdate(sql);
System.out.println("插入行数:"+row);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
st.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用statement修改对象
public class UpdateGrade {
//修改数据库数据
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
Grade g = new Grade(3, "大三", "即将面临就业,需要努力的时候");
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "root");
st = conn.createStatement();
String sql = "update grade set gname='"+g.getGname()+"',gdesc='"+g.getGdesc()+"' where gid="+g.getGid();
int row = st.executeUpdate(sql);
System.out.println("修改成功行数:"+row);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
st.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用statement删除对象
public class DeleteGrade {
//修改数据库数据
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "root");
st = conn.createStatement();
String sql = "delete from grade where gid=5";
int row = st.executeUpdate(sql);
System.out.println("删除成功行数:"+row);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
st.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用statement查询表,并返回ResultSet结果集
public class QueryGrade {
//使用statement对象查询数据库,并返回结果集对象
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.建立连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8", "root", "root");
st = conn.createStatement();
//准备查询语句,因为数据库查询结果会使用对象接收,一般查询全列
String sql = "select * from grade";
//使用st对象的,executeQuery(sql),并返回数据存储到ResultSet结果集中
rs = st.executeQuery(sql);
//遍历结果集内容
// 结果集中有两个方法 next()判断结果集中是否有下一行数据 如果有使用getXXX()获取指定类型的值
while(rs.next()) {
System.out.println("年级编号为:"+rs.getInt("gid")+", 名称为:"+rs.getString(2)+", 描述:"+rs.getString(3));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
rs.close();
st.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用Statement对象以拼接的方式做登录查询
但是在登录过程中,因为SQL的拼接,造成sql结构错误,导致查询出错这种错误叫做SQL注入
public class LoginStatement {
//SQL注入
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8", "root", "root");
st = conn.createStatement();
String name="admin",password="1234567 or 1=1";
//通过设置密码,与拼接sql的形式组合,在密码中注入了一个 or 1=1的SQL片段,这就是sql注入
String sql = "select * from user where name='"+name+"' and password="+password;
//执行查询方法,并返回rs结果集
rs = st.executeQuery(sql);
while(rs.next()) {
System.out.println("用户ID为:"+rs.getInt(1)+", 用户名:"+rs.getString(2)+", 密码为:"+rs.getString(3));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
rs.close();
st.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
PreparedStatement接口
preparedstatement接口是Statement的子接口,因为Statement容易被SQL注入,使用preparedstatement预编译SQL的方式解决Statement中SQL注入的问题
PreparedStatement添加数据
1.先准备sql
2.再获取Preparedstatement对象,并预编译SQL句(在创建对象的同时,先将SQL读入到pst对象中,所有需要填值的地方使用?占位符表示)
3.对占位符赋值,位置从1开始,如果没有占位符就不需要赋值
public class SaveGrade {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pst = null;
Grade grade = new Grade(5, "研究生", "没事就研究研究");
try {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.建立连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8", "root", "root");
//3.准备sql,?表示占位符,指定位置等待赋值
String sql = "insert into grade values(?,?,?)";
//4.获取到pst执行对象,预编译sql语句,保证sql不出问题
pst = conn.prepareStatement(sql);
//5.为占位符赋值,占位符的位置从1开始
pst.setInt(1, grade.getGid());
pst.setString(2, grade.getGname());
pst.setString(3, grade.getGdesc());
//6.执行sql并返回受影响行数,用于执行更新(增删改)调用executeUpdate()方法
//注意: 前面已经 预编译SQL了,这个位置只需要执行
int row = pst.executeUpdate();
System.out.println("插入行数:"+row);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
pst.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用PreparedStatement接口解决查询sql注入
public class LoginPst {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8", "root", "root");
String name="admin",password="1234567 or 1=1";
//通过设置密码,与拼接sql的形式组合,在密码中注入了一个 or 1=1的SQL片段,这就是sql注入
String sql = "select * from user where name=? and password=?";
pst = conn.prepareStatement(sql);
pst.setString(1, name);
pst.setString(2, password);
//执行查询方法,并返回rs结果集
rs = pst.executeQuery();
while(rs.next()) {
System.out.println("用户ID为:"+rs.getInt(1)+", 用户名:"+rs.getString(2)+", 密码为:"+rs.getString(3));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
rs.close();
pst.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
优化jdbc代码,把重复的代码抽取出来单独放在方法当中,把变的使用变量表示,从而可以实现简化代码的作用
/**
* JDBC操作的工具类 对jdbc的代码进行了优化,简化了代码 对于重复的代码就使用方法进行重用,从而减少了代码量
*
*
*/
public class JDBCTools2 {
private static String driver;
private static String url;
private static String user;
private static String password;
private static Connection con;
// 静态代码块,当初始化当前类时才会执行,并且只会执行一次
static {
try {
try {
//实例化一个Properties对象,该对象就可以读取properties配置文件
Properties p = new Properties();
//需要把jdbc.properties配置文件构造成一个流对象
//InputStream is = new FileInputStream("resources\\jdbc.properties");
InputStream is = JDBCTools2.class.getClassLoader().getResourceAsStream("jdbc.properties");
//把配置文件加载到Properties对象当中
p.load(is);
//就可以根据键获取到值
driver = p.getProperty("jdbc.driverclass");
url = p.getProperty("jdbc.url");
user = p.getProperty("jdbc.user");
password = p.getProperty("jdbc.password");
System.out.println("driver:"+driver);
System.out.println("url:"+url);
System.out.println("user:"+user);
System.out.println("password:"+password);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 加载驱动
Class.forName(driver);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 专门获得数据库连接的方法
*
* @return 数据库连接对象
*/
public static Connection getCon() {
//相当于变成了一个单例的了
try {
// 为空或者连接已经关闭了
if (con == null || con.isClosed()) {
con = DriverManager.getConnection(url, user, password);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return con;
}
/**
* 关闭数据库连接的方法
* @param con
* @param ps
* @param rs
*/
public static void closeAll(Connection con,Statement ps,ResultSet rs ) {
try {
// 关闭数据库连接, 释放数据库资源
if (con != null) {
con.close();
}
if (ps != null) {
ps.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 增删改的通用方法
* 可变类型参数 Object... parametes Object[] parametes
* @param sql
* @param parametes
* @return
*/
public static int executeUpdate(String sql,Object... parametes ) {
//得到一个数据库连接对象
Connection con = getCon();
PreparedStatement ps=null;
try {
ps = con.prepareStatement(sql);
//传递参数
//非空判断
if(parametes!=null&¶metes.length>0) {
for(int i=0;i<parametes.length;i++) {
ps.setObject(i+1, parametes[i]);
}
}
int iret = ps.executeUpdate();
return iret;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//关闭数据库连接资源
closeAll(con,ps,null);
}
return 0;
}
}