jdbc概念
java database connectivity java数据库连接,java语言操作数据库
jdbc本质
是官方定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(jdbc)编程,但真正执行的代码是驱动jar包中的实现类
jdbc快速入门
步骤
- 导入驱动jar包(连接什么数据库就导入什么类型的jar包)
- 复制mysql-connector-java-5.1.39-bin.jar到项目libs目录下
- 右键libs --> Add as Library
- 复制mysql-connector-java-5.1.39-bin.jar到项目libs目录下
- 注册驱动(告诉程序使用哪一个数据库的驱动jar包)
- 获取数据库连接对象 Connection(java代码与数据库之间的桥梁)
- 定义sql语句
- 获取执行sql语句的对象 Statement
- 执行sql,接收返回结果
- 处理结果
- 释放资源(先开的后关)
//1.导入驱动jar包
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/contest?useUnicode=true&characterEncoding=UTF-8", "user", "password");
//4.定义SQL语句
String sql="update hello set age=30 where id=1";
//5.获取执行SQL的对象Statement
Statement stmt = conn.createStatement();
//6.执行SQL
int result = stmt.executeUpdate(sql);
//7.处理结果
System.out.println(result);
//8.释放资源
stmt.close();
conn.close();
详解各个对象
DriverManager:驱动管理对象
1.注册驱动
static void registerDriver(Driver driver)
我们写代码使用:Class.forName("com.mysql.jdbc.Driver");
查看com.mysql.jdbc.Driver类的源码发现静态代码块(当类被加载进内存,静态代码块会自动执行),本质上还是DriverManager.registerDriver(Driver driver),这样写会提高类的独立性且方便
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
注意:mysql5之后的驱动jar包可以省略注册驱动的步骤
2.获取数据库连接对象
方法:static Connection getConnection(String url, String user, String password)
参数:
- url:指定连接的路径语法:
jdbc:mysql://ip地址(域名):端口/数据库名
例子:jdbc:mysql://localhost:3306/contest
注意:
- 如果连接的是本机的mysql服务器,并且mysql服务端口为3306,则url可以简写为
jdbc:mysql:///数据库名
- 在url后面设置编码,解决数据库中文乱码
jdbc:mysql:///contest?useUnicode=true&characterEncoding=UTF-8
- 如果连接的是本机的mysql服务器,并且mysql服务端口为3306,则url可以简写为
- user:用户名
- password:密码
Connection:数据库连接对象
1.获取执行SQL的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
2.管理事务
需要先把自动提交修改为手动提交
开启事务:setAutoCommit(boolean autoCommit)
调用该方法设置参数false(关闭自动提交),即开启事务
提交事务:commit()
回滚事务:rollback()
Statement:执行SQL的对象
1.执行SQL
-
boolean execute(String sql)
:可以执行任意的SQL
-
int executeUpdate(String sql)
:执行DML(insert、update、delete)语句、DDL(create,alter,drop)语句
返回值:影响的行数。返回值>0则执行成功,反之则执行失败
-
ResultSet executeQuery(String sql)
:执行DQL(select)语句
2.insert语句练习
Connection coon=null;
Statement stmt=null;
try {
Class.forName("com.mysql.jdbc.Driver");
coon = DriverManager.getConnection("jdbc:mysql:///contest?useUnicode=true&characterEncoding=UTF-8", "user", "password");
String sql="insert into hello values(null,'张三',20)";
stmt = coon.createStatement();
int result = stmt.executeUpdate(sql);
System.out.println(result);
if (result>0){
System.out.println("执行成功");
}else{
System.out.println("执行失败");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
/*避免空指针异常,因为try语句块中的每一句都有可能异常,那么stmt、coon会为null
而且无论如何资源都要关闭,将其放在finally中*/
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (coon!=null){
try {
coon.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
ResultSet:结果集对象,封装着查询结果
boolean next():游标移动到下一行,并判断当前行是否有数据(最后一行之后),有数据返回true,否则返回false。游标默认在第一行之前
getXxx(参数):获取数据(从一行中获取某个列的数据)
- Xxx:代表数据类型 如:int getInt(),String getString()
- 参数:
- int:代表列的编号,从1开始 如:getString(1)
- String:代表列名称。如:getInt("age")
- int:代表列的编号,从1开始 如:getString(1)
注意使用步骤:
- 游标向下移动一行
- 判断是否有数据
- 获取数据
select语句练习
数据库的表结构与java类相似
而表中的一行数据代表一个对象
定义一个方法,查询hello表的数据将其封装为对象,然后装载集合,返回。
- 定义Hello类
- 定义方法
public List<Hello> findAll(){}
- SQL语句
select * from hello;
//封装hello表数据的JavaBean
public class Hello {
private int id;
private String name;
private int age;
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Hello{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Jdbc {
public static void main(String[] args){
List<Hello> list = new Jdbc().findAll();
System.out.println(list);
System.out.println(list.size());
}
//查询所有Emp对象
public List<Hello> findAll(){
Connection coon=null;
Statement stmt=null;
ResultSet rs=null;
List<Hello> list=null;
try {
Class.forName("com.mysql.jdbc.Driver");
coon = DriverManager.getConnection("jdbc:mysql://localhost:3306/contest?useUnicode=true&characterEncoding=UTF-8", "user", "password");
stmt = coon.createStatement();
String sql="select * from hello";
rs = stmt.executeQuery(sql);
list=new ArrayList<>();
//遍历结果集,封装对象,装载集合
Hello hello=null;
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
/**
* 封装对象
* Hello hello = new Hello();
* 这里不使用上述代码,因为引用太多,浪费栈内存
* 不如在外面写 Hello hello=null;
* 再复用引用
*/
hello=new Hello();
hello.setId(id);
hello.setAge(age);
hello.setName(name);
list.add(hello);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(coon!=null){
try {
coon.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return list;
}
}
输出:
[Hello{id=1, name='ggbond', age=100}, Hello{id=2, name='??', age=20}, Hello{id=3, name='张三', age=20}]
3
抽取jdbc工具类:JDBCUtils
目的:简化书写
分析:
- 抽取注册驱动
- 抽取一个方法获取连接对象
需求:不想传递参数(麻烦),还得保证工具类的通用性
解决:配置文件(只需要改配置文件,代码不用动)
jdbc.properties(在src目录下)
url=jdbc:mysql:///contest
user=xxx
password=xxx
driver=com.mysql.jdbc.Driver
- 抽取一个方法释放资源
JDBCUtils.java
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
/**
* 文件的读取只需一次
* static{}静态代码块随着类被加载进内存自动执行,且只执行一次
*/
static{
//读取jdbc.properties配置文件,获取值
try {
//1.创建properties集合类
Properties pro = new Properties();
//获取src目录下文件的方式--> ClassLoader 类加载器(可以加载字节码文件进内存,并且可以获取src目录下资源的路径)
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();
//2.加载文件
pro.load(new FileReader(path));
//3.获取数据,并赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4.注册驱动
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
* 工具类方法为了方便使用一般都为静态方法
* @return connection连接对象
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
/**
* 释放资源
* 释放资源有两种情况
* 1.执行增删改操作,释放2个对象,statement对象,connection对象
* 2.执行查询操作,释放3个对象,resultSet对象,statement对象,connection对象
* 因此利用重载机制来实现
*/
public static void close(Connection conn, Statement stmt){
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public static void close(Connection conn, Statement stmt,ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
jdbc.java
public class Jdbc {
public static void main(String[] args){
List<Hello> list = new Jdbc().findAll();
System.out.println(list);
System.out.println(list.size());
}
//查询所有Emp对象
public List<Hello> findAll(){
Connection coon=null;
Statement stmt=null;
ResultSet rs=null;
List<Hello> list=null;
try {
coon = JDBCUtils.getConnection();
stmt = coon.createStatement();
String sql="select * from hello";
rs = stmt.executeQuery(sql);
list=new ArrayList<>();
//遍历结果集,封装对象,装载集合
Hello hello=null;
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
/**
* 封装对象
* Hello hello = new Hello();
* 这里不使用上述代码,因为引用太多,浪费栈内存
* 不如在外面写 Hello hello=null;
* 再复用引用
*/
hello=new Hello();
hello.setId(id);
hello.setAge(age);
hello.setName(name);
list.add(hello);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
JDBCUtils.close(coon, stmt, rs);
}
return list;
}
}
PreparedStatement:执行SQL的对象
1.sql注入问题
在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
- 输入用户任意,输入密码:a' or 'a'='a
- sql:select * from user where username ='sadafs' and password = 'a' or 'a'='a'
2.使用PreparedStatement对象解决sql注入问题
- Statement对象执行静态的sql语句
- PreparedStatement对象执行预编译(动态)的sql语句
- 预编译的sql:参数使用?作为占位符
3.步骤
- 导入驱动jar包
- 注册驱动
- 获取数据库连接对象 Connection
- 定义sql语句
注意:sql的参数使用?作为占位符。如:select * from user where username =? and password = ?
- 获取执行sql语句的对象 PreparedStatement conn.preparedStatement(String sql)
- 给?赋值
方法:setXxx(参数1,参数2)
Xxx:数据类型
参数1:?的位置编号。从1开始
参数2:?的值
- 执行sql,接收返回结果。无需传递sql语句
- 处理结果
- 释放资源
注意:后期都会使用PreparedStatement来完成增删改查的所有操作
- 可以防止SQL注入
- 效率更高
示例代码
//查询数据库是否有某个用户,工具类JDBCUtils.java
public class Jdbc {
public static void main(String[] args) {
//键盘录入,接收name和age
Scanner sc = new Scanner(System.in);
System.out.println("请输入name");
String name = sc.nextLine();
System.out.println("请输入age");
String age = sc.nextLine();
//调用方法
boolean flag = new Jdbc().find(name, age);
//判断
if (flag) {
//存在
System.out.println("存在用户");
} else {
System.out.println("不存在用户");
}
}
public boolean find(String name, String age) {
Connection coon = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
//获取连接
coon = JDBCUtils.getConnection();
//定义SQL
String sql = "select * from hello where name =? and age = ?";
//获取执行SQL的PreparedStatement对象
pstmt = coon.prepareStatement(sql);
//sql在执行前要给?赋值
pstmt.setString(1, name);
pstmt.setString(2, age);
//执行查询,不需要传递SQL
rs = pstmt.executeQuery();
//判断
return rs.next();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.close(coon, pstmt, rs);
}
return false;
}
}
JDBC控制事务
1.事务
一个包含多个SQL语句的业务操作。如果这个业务操作被事务管理,里面的SQL语句要么同时成功,要么同时失败
2.操作
- 开启事务
- 提交事务
- 回滚事务
3.使用Connection对象来管理事务
- 开启事务:
setAutoCommit(boolean autoCommit)
调用该方法设置参数false(关闭自动提交),即开启事务
在执行SQL之前开启事务
- 提交事务:
commit()
当所有SQL都执行完提交事务
- 回滚事务:
rollback()
在catch中回滚事务(一旦有异常就会回滚)