JDBC概述
JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现。
JDBC编程步骤
装载相应数据库的JDBC驱动并进行初始化
导入专用的jar包
- 访问MySQL数据库需要用到第三方的类,这些第三方的类都被压缩在jar包里。
- mysql-connector-java-5.0.8-bin.jar
初始化驱动
通过初始化驱动类com.mysql.jdbc.Driver
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class.forName是把这个类加载到JVM中,加载的时候,就会执行其中的静态初始化块,完成驱动的初始化的相关工作。
建立JDBC和数据库之间的Connection连接
需要提供:
- 数据库服务端的IP地址:127.0.0.1
- 数据库的端口号:3306
- 数据库名称:exam
- 编码方式:UTF-8
- 账号:root
- 密码:admin
Connection c =DriverManager.getConnection(“jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8”, “root”, “admin”);
Connection是与特定数据库连接回话的接口,使用的时候需要导包,而且必须在程序结束的时候将其关闭。getConnection方法也需要捕获SQLException异常。
- 在进行数据库的增删改查的时候都需要与数据库建立连接,所以可以在项目中将建立连接写成一个工具方法,用的时候调用即可
/**
* 取得数据库的连接
* @return 一个数据库的连接
*/
public static Connection getConnection(){
Connection conn = null;
try {
//初始化驱动类com.mysql.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8","root", "admin");
//该类就在 mysql-connector-java-5.0.8-bin.jar中,如果忘记了第一个步骤的导包,就会抛出ClassNotFoundException
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
创建Statement或者PreparedStatement接口,执行SQL语句
使用Statement
-
Statement接口创建之后,可执行SQL语句,完成对数据库的增删改查。其中,增删改查只需改变SQL语句的内容就能完成;而查询较复杂。在Statement中使用字符串拼接的方式,该方式存在语句复杂、容易犯错等缺点。
-
Statement在实际过程中使用的非常少
-
字符串拼接方式的SQL语句是非常繁琐的,中间有很多单引号和双引号的混用,容易出错
Statement s = conn.createStatement();
// 准备sql语句
// 注意: 字符串要用单引号'
String sql = "insert into t_courses values(null,"+"'数学')";
//在statement中使用字符串拼接的方式,这种方式存在诸多问题
s.execute(sql);
System.out.println("执行插入语句成功");
使用PreparedStatement
PreparedStatement需要根据sql语句创建
-
使用PreparedStatement时,他的sql语句不再采用字符串拼接的方式,而是采用“ ?” 占位符的方式。这种方式除了可以避免拼接字符串的繁琐之外,还能提高性能。每次SQL语句都是一样的,java类就不会再次编译,这样能显著提高性能。
-
String sql = “update t_course set course_name =? where course_id=?”;
然后使用PreparedStatement接口中的set方法给占位符进行赋值。[索引是从1开始的]
//预编译
pstmt = (PreparedStatement) conn.prepareStatement(sql);
//利用Preparedstatement的set方法给占位符赋值
pstmt.setString(1, courseName);
pstmt.setInt(2, courseId);
pstmt.executeUpdate();
增删改都使用pstmt.executeUpdate(); 语句进行SQL语句的提交 ,查找则使用 executeQuery() 方法
在添加的过程中,如果添加的数据比较大,可以使用批量添加。
for(int i=1;i<100;i++){
pstmt.setInt(1,8000+i);
pstmt.setString(2,"赵_"+i);
pstmt.addBatch();
//批量更新
if(i%10==0){
pstmt.executeBatch();
}
}
查询操作
查询表student中数据id,name
public class Ch02 {
public static void main(String[] args) throws SQLException {
Ch02 ch02 = new Ch02();
List<Student> stuList = ch02.findCourseList();
System.out.println(stuList);
}
public List<Student> findCourseList() throws SQLException {
String sql = "select * from stu order by id";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//创建集合对象存放查询到的数据
List<Student> stuList = new ArrayList<>();
try {
conn = JdbcUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
rs = (ResultSet) pstmt.executeQuery();
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
Student student = new Student(name,id);
stuList.add(student);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn);
JdbcUtil.close(pstmt);
}
return stuList;
}
}
class Student{
private String name;
private int id;
public Student(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
处理和显示结果
执行查询语句,并把结果返回给集合ResultSet
ResultSet rs = s.executeQuery(sql);
利用While(ResultSet.net()){…}循环将ResultSet中的结果遍历出来。
ResultSet.getxxx();
-
get方法里可以填写属性值或者填写该属性在数据表中的列号
-
一般不推荐使用列号,因为一段数据表中各个属性值顺序会发生变化
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
//记录对应的对象
Student student = new Student(name,id);
stuList.add(student);
}
释放资源
在JDBC编码的过程中我们创建了Connection、ResultSet等资源,这些资源在使用完毕之后是一定要进行关闭的。关闭的过程中遵循从里到外的原则。
创建工具类
为了使代码更简单,增加复用性,将一些频繁使用的操作放到一个工具类里
- 使用反射
- 连接数据库getConnection()
- 释放资源close()
public class JdbcUtil {
public static Connection getConnection() throws ClassNotFoundException, SQLException, IOException {
Properties properties = new Properties();
//读取外部的p文件
properties.load(Test01.class.getClassLoader().getResourceAsStream("jdbc.properties"));
//System.out.println(properties);
String url = properties.getProperty("mysql.url");
String driverName = properties.getProperty("mysql.driverName");
String username = properties.getProperty("mysql.username");
String password = properties.getProperty("mysql.password");
Class clazz = Class.forName(driverName);
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
/**
* 封装三个关闭方法
* @param pstmt
*/
public static void close(PreparedStatement pstmt){
if(pstmt != null){ //避免出现空指针异常
try{
pstmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
public static void close(Connection conn){
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public static void close(ResultSet rs){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public static void close(Statement stmt){
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
Statement和PreparedStatement的异同及优缺点
-
同:两者都是用来执行SQL语句的
-
异:PreparedStatement需要根据SQL语句来创建,它能够通过设置参数,指定相应的值,不是像Statement那样使用字符串拼接的方式
PreparedStatement的优点
使用参数设置,可读性好,不易记错。在statement中使用字符串拼接,可读性和维护性比较差
具有预编译机制,性能比statement更快
能够有效防止SQL注入攻击
execute和executeUpdate的区别
-
相同点:二者都能执行增删改查
-
不同点:
- execute可以执行查询语句,然后通过getResult把结果取出来。esecuteUpdate不能查询语句
- execute返回Boolean类型,true表示执行的是查询语句,false表示执行的insert、delete、update等。executeUpdate的返回值是int,表示有多少条数据受到了影响
案例
模拟登录功能。在前台输入用户名和密码,后台判断信息是否正确,并给出前台反馈信息,前台输出反馈信息。
- 创建数据库表 account 包含(aid,username,password)
- 开发前台代码 用户操作的页面
- 工具类JdbcUtil
public class Test02 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
Account account = findAccount(username,password);
System.out.println(account != null? "登录成功":"登录失败");
}
public static Account findAccount(String name,String pwd){
//sql语句
String sql = "select * from account where username = ? and password = ?";
// 连接
Connection conn = null;
//执行SQL语句
PreparedStatement pstmt = null;
//执行查询语句,并把结果返回给集合ResultSet
ResultSet rs = null;
Account login = null;
try {
//建立连接
conn =JdbcUtil.getConnection();
//预编译
pstmt = (PreparedStatement) conn.prepareStatement(sql);
//给占位符赋值,下标是从1开始的
pstmt.setString(1,name);
pstmt.setString(2,pwd);
//执行查询语句,并把结果返回给集合ResultSet
rs = (ResultSet) pstmt.executeQuery();
//判断是否有值
if (rs.next()){
login = new Account(rs.getString("username"),rs.getString("password"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn);
JdbcUtil.close(pstmt);
}
return login;
}
}
class Account{
private String username;
private String password;
public Account() {
}
public Account(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "Account{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}