目录
5)reperPreparedStatement:也是执行sql对象,但比Statement功能强大
1.JDBC的概念
JDBC=Java DataBase Connectivity,使用同样的Java代码操作不同的数据库。
本质:是官方定义的一套操作关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动的jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
2.JDBC编码步骤
- 导入驱动jar包 (此处相关jar包见https://blog.csdn.net/weixin_44187963/article/details/104864821)
- 编写代码注册驱动
- 获取数据库的连接对象 connection
- 定义SQL语句
- 获取SQL语句对象 statement
- 执行SQL,接收返回结果
- 处理返回结果
- 释放资源
public class JdbcDome01 {
public static void main(String[] args) throws Exception {
//1.导入JAR包
//2.注册驱动,在mysql5版本之后可以省略这个步骤,因为jar包里配置,系统回会自动注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//3.获取数据库的连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/TestDB?serverTimezone=UTC","root","123456");
//4.定义sql语句
String sql = "update person set id = 161004 where id = 4";
//5.获取执行sql的对象 statement
Statement stat = conn.createStatement();
//6.执行sql
int info = stat.executeUpdate(sql);
//7.处理结果
System.out.println(info);
//8.释放资源
conn.close();
stat.close();
}
}
当然,这块只是梳理流程,并没有去抓取异常。而且关闭资源的代码理应在异常处理的finally块中,才会不论前面是否异常最终都会执行到finally,这样以免产生内存的泄露。所以代码可以改为:
public class JdbcDome03 {
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/TestDB?serverTimezone=UTC","root","123456");
String sql = "update person set id = 161005 where id = 5";//修改表sql
sta = con.createStatement();
int count = sta.executeUpdate(sql);
if(count > 1){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
这样一看,很明显每一个操作都是在重复获取连接、关闭资源等基础操作,所以这块我们可以在utils包封装一个工具类出来,至于工具类的使用放在后面实现登录的代码里:
/**
* JDBC工具类要有两个方法,一是注册驱动获取链接,二是释放资源
* 注意:工具类的方法一般都是静态方法
*/
public class JdbcUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
//读取配置文件:期望文件的读取只读取一次,而静态代码块随着类的加载而加载,所以用静态代码块读取配置文件中的资源
static{
Properties properties = new Properties();
try {
//获取src下文件路径---->Class.loader 类加载器
ClassLoader classLoader = JdbcUtils.class.getClassLoader();
URL res = classLoader.getResource("Jdbc.properties");
String path = res.getPath();
//加载文件
properties.load(new FileReader(path));
//获取属性,赋值
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
driver = properties.getProperty("Driver");
//加载驱动
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,user,password);
}
//释放资源,因为有两种情况,所以写两个方法实现方法的重载
public static void release(Statement state,Connection conn){
if(state != null){
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void release(ResultSet res,Statement state,Connection conn){
if(res != null){
try {
res.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(state != null){
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.详解各个对象
1)DriverManager:驱动管理对象
1. 注册驱动 static void registerDriver(Driver driver)
注册与给定的驱动程序DriverManager
但我们代码中用Class.forName("com.mysql.cj.jdbc.Driver");
有什么关系呢?
通过源码发现在com.mysql.cj.jdbc.Driver下有个静态代码块,代码块里使用了registerDriver
2. 获取数据库连接 static Connection getConnection(String url,Stirng user,String password)
这里url = jdbc:mysql://IP:端口号/数据库名
2)Connection:数据库连接对象
1.获取执行sql的对象 Statement createStatement();
PrepareStatement prepareStatement(String sql);
2.管理事务
开启事务:setAutoCommit(boolean autuCommit);参数设置为false
提交事务:commit();
回滚事务:rollbacl();
3)Statement:执行sql的对象
1. boolean execute(String sql); 可以执行任意的sql
2. int executeUpdate(String sql);
执行DML(insert,update,delete)语句、DLL(表的create、drop、alter)语句,返回值是影响的行数。
3. ResultSet executeQuery(String sql); 执行DQL(select)语句
这块代码就不贴出来了,就是注意不同的sql语句后面执行的时候调用不同的方法就行了。
4)ResultSet:结果集对象,封装返回结果的
next(); 游标向下移动一行,最开始默认指向的是标题栏,所以就有下面代码中的res.next()
getXxx(参数); 获取返回值,比如int getInt()
get方法的参数有两种情况:
int代表列的编号,从1开始,比如getString(1) 获取第一列的值;
String代表列的名称,比如getString("name")
使用步骤:
1.游标向下移动一行
2.判断是否有数据
3.获取数据
此时熟悉前面这部分的使用,我们写一个实现登录逻辑的代码,其中数据库的连接及关闭资源都使用了工具类:
/**
* 需求:通过键盘输入用户名和密码
* 判断用户是否登录成功
*/
public class JdbcLogin {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
boolean flag = new JdbcLogin().logIn2(name, password);
if(flag){
System.out.println("登陆成功!");
}else{
System.out.println("登陆失败...");
}
}
//登录方法
public boolean logIn(String usename,String password){
//连接数据库判断是否登陆成功
Connection connection = null;
Statement statement = null;
ResultSet res = null;
if(usename == null || password == null){
return false;
}
try {
connection = JdbcUtils.getConnection();
String sql = "select * from user where username = '"+usename+"' and password = '"+password+"'";
statement = connection.createStatement();
res = statement.executeQuery(sql);
return res.next();//返回的就是一个Boolean类型值,代替了if语句
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(res,statement,connection);
}
return false;
}
}
5)reperPreparedStatement:也是执行sql对象,但比Statement功能强大
1.SQL注入问题:在拼接sql时会有sql的特殊关键字参与字符串的拼接,会造成安全性问题
2.解决SQL注入问题:使用PreparedStatement对象来解决
3.预编译SQL:参数使用?作为占位符
4.使用步骤在sql语句时:
1)用?作为占位符写sql语句
2)在PreparedStatement(sql)方法中要传入sql作为参数
3)给占位符?赋值:setXxx(参数1,参数2);
参数1---?的位置编号(从1开始)
参数2---?的值
4)在下一步执行sql接收返回结果时就不需要传递sql参数了
之前写的sql语句是静态sql,用reperPreparedStatement获取执行对象后是这样的:对比上一个登录实现代码来理解:
public class JdbcLogin {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
boolean flag = new JdbcLogin().logIn2(name, password);
if(flag){
System.out.println("登陆成功!");
}else{
System.out.println("登陆失败...");
}
}
/**
* 使用preparedstatement的登录方法的实现
*/
public boolean logIn2(String username,String password){
//连接数据库判断是否登陆成功
Connection connection = null;
PreparedStatement statement = null;
ResultSet res = null;
if(username == null || password == null){
return false;
}
try {
connection = JdbcUtils.getConnection();
String sql = "select * from user where username = ? and password = ?";
statement = connection.prepareStatement(sql);//此处传参
//注意给?赋值
statement.setString(1,"username");
statement.setString(2,"password");
res = statement.executeQuery();//此处不需要传参
return res.next();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(res,statement,connection);
}
return false;
}
}