JDBC:是Java程序访问数据库的一系列标准接口。Java程序访问数据库不是通过与TCP建立连接,而是通过Java接口建立连接访问数据库的,Java接口通过Java驱动实现与数据库的连接。Java驱动是数据库开发商提供的一套实现Java接口的实现类,要连接某个数据库只需将其Java驱动导入,通过Java接口进行访问数据库。
JDBC连接
JDBC是一套标准接口,位于java.sql包中,这里面都是接口,并不能被实例化,所以就需要引入对这些接口进行实例化的实现类,Java驱动就是对这些接口进行实例化的实现类。Java接口提供访问数据库的统一接口,具体咋样去实现对数据库的连接,就需要根据不同的数据库厂商提供实现类去实现了。
JDBC中提供了与数据库连接的接口:Connection接口,通过DriverManager中的getConnection()传入参数"数据库连接符(包含数据库名,主机地址,端口号数据库,配置参数)",“用户名”,“密码”,返回一个Connection,DriverManager驱动管理类会找到所有的驱动,并根据传入的"数据库连接字符串"找到对应的数据库驱动。
1、建立数据库连接
public static void main(String[] args) {
//低版本的驱动类,需要手动加载驱动类
//反射的方式加载驱动类
//Class.forName("com.myssql.cj.jdbc.Driver");
//驱动管理类
//(高版本中)DriverManager会自动加载驱动包中“驱动类”:com.myssql.cj.jdbc.Driver
//并返回驱动包中Connection接口的实现类对象
final String url = "jdbc:mysql://localhost:3306/test_db";//数据库连接字符串
final String db_user_name = "用户名";//用户名
final String db_user_pass = "密码";//密码
//创建MySQL数据库连接(需要释放数据库连接)
try(Connection con = DriverManager.getConnection(url, db_user_name,db_user_pass)){
//输出
System.out.println("数据库连接:"+con);
} catch (SQLException e) {
e.printStackTrace();
}
}
2、JDBC查询
创建Statement对象,执行数据库操作
通过Connection提供的createStatement()创建Statement对象,用于执行数据库操作。
再通过Statement对象中executeQuery()并给其传入sql语句,获取ResultSet结果集。
再通过调用next()获取数据库表中得每一行数据。
最后,按照字段编号或字段名称读取字段值。
public static void main(String[] args) {
final String db_url = "jdbc:mysql://localhost:3306/test_db";//数据库连接字符串
final String db_user_name = "用户名";
final String db_user_pass = "密码";
//1.创建JDBC连接
try(Connection con = DriverManager.getConnection(db_url, db_user_name,db_user_pass)){
//2.创建Statement对象,执行数据库操作
try(Statement st = con.createStatement();){
//3.执行查询,并获取ResultSet结果集
try(ResultSet rs = st.executeQuery("select * from website");){
//4.遍历结果集
while(rs.next()) {
//方式1
//读取当前行的每个字段值
//按照字段编号读取:从1开始
int id = rs.getInt(1);
String name = rs.getString(2);
//方式2
//按照字段名称读取
int id = rs.getInt("id");
String name = rs.getString("name");
String url = rs.getString("url");
//输出
System.out.println("编号:"+id);
System.out.println("网站名称:"+name);
System.out.println("网站地址:"+url);
System.out.println();
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
SQL注入
创建Statement对象执行数据库查询时,sql语句是拼接出来的,这样就很容易使有些别有用心的用户编造一个特殊的字符串(用户名或密码)拼接到sql语句中,拼接出一个正确的sql语句。此时就会引发SQL注入问题。
public static void main(String[] args) {
final String db_url = "jdbc:mysql://localhost:3306/test_db";//数据库连接字符串
final String db_user_name = "用户名";
final String db_user_pass = "密码";
//1.创建JDBC连接
try(Connection con = DriverManager.getConnection(db_url, db_user_name,db_user_pass)){
//2.创建Statement对象,执行数据库操作
try(Statement st = con.createStatement();){
String phoneName = "12387346789";
//特殊的字符串
String loginPassword = "'or 1=1 or'";
//拼接sql语句
StringBuilder sql = new StringBuilder();
sql.append("select * from user_info where phone_number = '");
sql.append(phoneName);
sql.append("' and login_password = '");
sql.append(loginPassword);
sql.append("'");
System.out.println("完整的SQL语句:"+sql);
//3.执行查询,并获取ResultSet结果集
try(ResultSet rs = st.executeQuery(sql.toString());){
//4.处理查询结果集
String realName = "";
if(rs.next()) {
realName = rs.getString("user_real_name");
}else {
realName = "登录失败!";
}
System.out.println(realName);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
PreparedStatement对象
要想解决SQL注入的问题,可以使用PreparedStatement
使用PreparedStatement对象,执行数据库操作时,是将完整的sql语句传给PreparedStatement对象的,要传入的数据使用?作为占位符,这样就可以保证每次传给数据库都是完整的sql语句,只是占位符处的数据不一样,还能提高SQL预编译的执行效率。
public static void main(String[] args) {
final String db_url = "jdbc:mysql://localhost:3306/test_db";//数据库连接字符串
final String db_user_name = "用户名";
final String db_user_pass = "密码";
//1.创建JDBC连接
try(Connection con = DriverManager.getConnection(db_url, db_user_name,db_user_pass)){
//2.创建PreparedStatement对象,执行数据库操作
//作用1:避免SQL注入
//作用2:SQL预编译提高执行效率
String sql = "select * from user_info where phone_number = ? and login_password = ?";
try(PreparedStatement pst = con.prepareStatement(sql);){
//3.处理问号占位符
String phoneName = "12387346789";
String loginPassword = "abc";
//设置?处的数据
//数据类型与数据库一致
pst.setString(1, phoneName);
pst.setString(2, loginPassword);
System.out.println("完整的SQL语句:"+sql);
//4.执行查询,并获取ResultSet结果集
try(ResultSet rs = pst.executeQuery()){
//5.处理查询结果集
String realName = "";
if(rs.next()) {
realName = rs.getString("user_real_name");
}else {
realName = "登录失败!";
}
System.out.println(realName);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
3、向数据库插入数据
将"插入"sql语句传给PreparedStatement对象,再处理占位符,最后调用executeUpdate()返回影响行数。
执行查询sql语句用executeQuery(),执行增加(删除、修改)sql语句用executeUpdate()。
public static void main(String[] args) {
final String db_url = "jdbc:mysql://localhost:3306/test_db";//数据库连接字符串
final String db_user_name = "root";
final String db_user_pass = "123456";
//1.连接
try(Connection con = DriverManager.getConnection(db_url,db_user_name,db_user_pass);){
//2.数据库操作(SQL语句)
String sql = "insert into user_info(user_real_name,phone_number,login_password,last_login_time) values(?,?,?,now())";
try(PreparedStatement pst = con.prepareStatement(sql)){
//处理问号占位符
pst.setString(1, "刘三");
pst.setString(2, "12356897854");
pst.setString(3, "abc123");
//3.执行(返回影响行数)
int row = pst.executeUpdate();
//插入一条数据,所以返回的行数为1
if(row == 1) {
System.out.println("添加成功!");
}else {
System.out.println("添加失败!");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
4、向数据库插入数据并获取自增主键值
将prepareStatement()方法中的参数"autoGeneratedKeys"设置为"Statement.RETURN_GENERATED_KEYS",再调用PreparedStatement对象中的getGeneratedKeys()获取自增主键值。
public static void main(String[] args) {
final String db_url = "jdbc:mysql://localhost:3306/test_db";//数据库连接字符串
final String db_user_name = "用户名";
final String db_user_pass = "密码";
//1.连接数据库
try(Connection con = DriverManager.getConnection(db_url,db_user_name,db_user_pass);){
//2.数据库操作(SQL)
String sql = "insert into user_info(user_real_name,phone_number,login_password,last_login_time) values(?,?,?,now())";
//设置PreparedStatement允许返回自动生成的主键值
try(PreparedStatement pst = con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS)){
//3.执行
//处理问号占位符
pst.setString(1, "光头强");
pst.setString(2, "18169873256");
pst.setString(3, "123456abc");
int row = pst.executeUpdate();
//如果添加成功,获取自增主键值
if(row == 1) {
//查询自动生成的主键值
try(ResultSet rs = pst.getGeneratedKeys()){
//指向新添加的那一行,并获取值
if(rs.next()) {
int id = rs.getInt(1);
System.out.println("添加成功!");
System.out.println("新用户的主键值为:"+id);
}
}
}else {
System.out.println("添加失败!");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
5、修改、删除
修改(删除)操作与添加操作差不多的执行过程,只是sql语句不一样,PreparedStatement 对象调用executeUpdate()执行sql语句,并返回影响的行数。