JavaWeb基础 - 跟JDBC说拜拜!
大家好,我是一只学弱狗,记录学习的点点滴滴!
优质文章
- 一张黄图的故事
- JavaSE练习项目
- 我是菜鸟、我小试牛刀
- linux指令太多记不住?小白看这篇就够了!
优质专栏
- 数据库就该这样学
- 爪哇外步篇
谨以此篇博客,结束时长整8个月的寒暑假生活。
畅聊JDBC
- JDBC
- 概念
- 使用步骤
- 代码演示
- 相关对象详解
- 疑问猜测
- DriverManager
- 概念
- 功能
- 注册驱动
- 获取数据库连接
- Connection
- 概念
- 功能
- 获取执行sql的对象
- 管理事务
- Statement
- 概念
- 常用方法
- ResultSet
- 概念
- 常用方法
- 案例:登陆验证功能
- 案例思考
- 事务管理及演示
- 什么是事务?
- 模拟:转账
- 数据库连接池
- 概念
- 剖析
- C3P0数据库连接池
- 使用步骤
- Druid数据库连接池
- 使用步骤
- Spring JDBC
- 概念
- 常用方法
JDBC
概念
JDBC,是Java Database Connectivity的缩写,即Java数据库连接,先看搜狗百科上的解释:JDBC是一种用于执行SQL语句的Java API,由一组用Java语言编写的类和接口组成,它可以为多种关系型数据库提供统一访问,据此可以构建更高级的工具和接口,实现了所有这些面向标准的目标并且具有简单,严格类型定义且高性能实现的接口。
上面的解释我勉强接受,用简单的语言我们这样来描述:它是Sun公司定义的一套操作所有关系型数据库的规则,各个数据库厂商,像MySQL、SQL Server等等,他们自己去实现这些接口,提供数据库的驱动jar包,我们可以按照Sun公司的规则(接口)编程。
使用步骤
- 导入驱动jar包(或者使用maven构建)
- 注册驱动
- 获取数据库连接对象
- 定义sql语句
- 获取执行sql语句的对象
- 执行sql语句,接收返回结果
- 处理结果
- 释放资源
Maven依赖
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.37</version>
</dependency>
代码演示
public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;//1.导入jar包try {//2.注册驱动Class.forName("com.mysql.jdbc.Driver");//3.获取连接对象connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "mysql");//4.定义sql语句String sql = "select * from tbl_user";//5.获取执行sql语句的对象statement = connection.createStatement();//6.执行sql语句resultSet = statement.executeQuery(sql);//7.处理结果while (resultSet.next()) {String username = resultSet.getString("username");String password = resultSet.getString("password");System.out.println(username + " : " + password);}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();} finally {//8.释放资源if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}}
相关对象详解
疑问猜测
看过了上面的代码,有小伙伴不禁会问,注册驱动,不就是通过反射加载Driver类进内存么,怎么就注册驱动了啊?
其实刚开始,我也有这样的疑问,不妨打开源码看一下
噢,原来这儿是一个静态代码块,懂了懂了。
DriverManager
概念
驱动管理对象
功能
注册驱动
在Driver的静态代码块中,我们发现,通过DriverManager的静态方法registerDriver来注册的驱动
获取数据库连接
public static Connection getConnection(String url,String user,String password)
- url:指定连接路径
- user:用户名
- password:密码
Connection
概念
数据库连接对象
功能
获取执行sql的对象
Statement createStatement() //执行静态sql
PreparedStatement prepareStatement(String sql) //执行预编译sql
管理事务
//开启事务
void setAutoCommit(boolean autoCommit) //调用该方法设置参数为false,即开启事务
//提交事务
void commit()
//回滚事务
void rollback()
Statement
概念
用于执行静态sql语句并返回其生成结果的对象
常用方法
int executeUpdate(String sql) //执行增删改语句,返回影响的行数
ResultSet executeQuery(String sql) //执行查语句,返回ResultSet结果集对象
ResultSet
概念
结果集对象,封装查询结果
常用方法
boolean next() //游标向下移动一行,判断当前行是否是数据行,如果是数据行则返回true,否则返回false
XXX getXXX() //获取数据,该方法为重载方法,可以传入列数,也可以传入列名
案例:登陆验证功能
任何理论都离不开实践!
需求:用户在控制台输入用户名和密码,核对其正确性,整个过程存储为日志信息。
public static void main(String[] args) {//待输入用户名String username = null;//待输入密码String password = null;//获取键盘输入流对象Scanner in = new Scanner(System.in);//日期对象Date date = new Date();//日期格式化对象SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");//缓冲字符输出流BufferedWriter bw = null;//数据库连接对象Connection connection = null;//执行sql语句对象Statement statement = null;//结果集对象ResultSet resultSet = null;try {//获取类加载器ClassLoader classLoader = Demo.class.getClassLoader();//获取指定资源的URL路径URL resource = classLoader.getResource("conf/log.txt");//获取指定资源的绝对路径String path = resource.getPath();//文件字符输出流 追加字符FileWriter fw = new FileWriter(path, true);bw = new BufferedWriter(fw);System.out.println("用户名:");username = in.nextLine();System.out.println("密码:");password = in.nextLine();connection = JDBCUtils.getConnection();String sql = "select * from tbl_user where username = '" + username + "' and password = '" + password + "' ";System.out.println(sql);statement = connection.createStatement();resultSet = statement.executeQuery(sql);String format = sdf.format(date);bw.newLine();bw.write("时间:" + format);bw.newLine();bw.write("用户名:" + username);bw.newLine();bw.write("密码:" + password);bw.newLine();if (resultSet.next()) {System.out.println("登录成功!");bw.write("登录成功!");} else {System.out.println("登录失败!");bw.write("登录失败!");}bw.newLine();bw.write("--------------------------------------");} catch (FileNotFoundException e) {System.out.println("未发现指定文件");System.exit(-1);} catch (SQLException e) {System.out.println("获取statement对象异常");System.exit(-1);} catch (IOException e) {System.out.println("IO读写异常");System.exit(-1);} finally {JDBCUtils.close(connection, statement, resultSet);try {bw.close();} catch (IOException e) {e.printStackTrace();}}}
注意到,有个JDBCUtils类,我们看下这个类
public class JDBCUtils {private static String driver = null;private static String url = null;private static String user = null;private static String password = null;static {try {Properties properties = new Properties();/*** 获取src路径下的文件的方式-->ClassLoader 类加载器*/properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("conf/jdbc.properties"));driver = properties.getProperty("driver");url = properties.getProperty("url");user = properties.getProperty("user");password = properties.getProperty("password");Class.forName(driver);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}public static Connection getConnection() {try {Connection connection = DriverManager.getConnection(url,user,password);return connection;} catch (SQLException e) {e.printStackTrace();return null;}}public static void close(Connection connection, Statement statement) {if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(connection!=null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}public static void close(Connection connection, Statement statement, ResultSet resultSet){close(connection,statement);if(resultSet!=null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}
}
仔细观察,提高了代码的复用性,通过配置文件修改数据库的属性,是不是值得我们学习呢?
案例思考
上面的代码,还是很不错的,但是也面临着一个问题,sql注入问题
纳尼?不可思议,观察sql语句,对于聪明的你我就不做更多的解释了?如何解决?PreparedStatement,之前我们说过,它是一个执行预编译sql的对象,如何操作呢?
首先原先的步骤得先发生下改变
- 导入驱动jar包
- 注册驱动
- 获取数据库连接对象 Connection
- 定义sql语句,注:sql的参数使用?来代替
- 获取执行sql语句的对象PreparedStatement
- 给占位符?赋值
- 执行sql语句,接收返回结果
- 处理结果
- 释放资源
于是,我们有个加强版
public static void main(String[] args) {String username = null;String password = null;Scanner in = new Scanner(System.in);Date date = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");BufferedWriter bw = null;Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {ClassLoader classLoader = DemoPlus.class.getClassLoader();URL resource = classLoader.getResource("conf/log.txt");String path = resource.getPath();FileWriter fw = new FileWriter(path, true);bw = new BufferedWriter(fw);/* FileOutputStream fos = new FileOutputStream(path,true);OutputStreamWriter osw = new OutputStreamWriter(fos);BufferedWriter bw = new BufferedWriter(osw);*/System.out.println("用户名:");username = in.nextLine();System.out.println("密码:");password = in.nextLine();connection = JDBCUtils.getConnection();String sql = "select * from tbl_user where username = ? and password = ?";preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1,username);preparedStatement.setString(2,password);resultSet = preparedStatement.executeQuery();String format = sdf.format(date);bw.newLine();bw.write("时间:" + format);bw.newLine();bw.write("用户名:" + username);bw.newLine();bw.write("密码:" + password);bw.newLine();if (resultSet.next()) {System.out.println("登录成功!");bw.write("登录成功!");} else {System.out.println("登录失败!");bw.write("登录失败!");}bw.newLine();bw.write("--------------------------------------");} catch (FileNotFoundException e) {System.out.println("未发现指定文件");System.exit(-1);} catch (SQLException e) {System.out.println("获取statement对象异常");System.exit(-1);} catch (IOException e) {System.out.println("IO读写异常");System.exit(-1);} finally {JDBCUtils.close(connection, preparedStatement, resultSet);try {bw.close();} catch (IOException e) {e.printStackTrace();}}}
我们再来sql注入一次,没门。。。
事务管理及演示
什么是事务?
指一个包含多个步骤的业务操作,如果这个业务被事务管理,则这多个步骤要么同时执行成功,要么同时失败。
模拟:转账
构建数据库表
我们的需求是张三给李四转1000元,期望结果张三余额是4000元,李四余额是6000元。
public static void main(String[] args) {//获取数据库连接对象Connection connection = JDBCUtils.getConnection();try {//开启事务connection.setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}//定义sql语句String sql = "update tbl_account set balance = balance + ? where id = ? ";//获取执行预编译sql语句的PreparedStatement对象PreparedStatement preparedStatement1 = null;PreparedStatement preparedStatement2 = null;try {preparedStatement1 = connection.prepareStatement(sql);preparedStatement1.setDouble(1,-1000);preparedStatement1.setInt(2,1);preparedStatement2 = connection.prepareStatement(sql);preparedStatement2.setDouble(1,+1000);preparedStatement2.setInt(2,2);int count1 = preparedStatement1.executeUpdate();//此处发生错误int a = 3/0;int count2 = preparedStatement2.executeUpdate();//提交事务connection.commit();if(count1>0 && count2>0){System.out.println("转账成功!");}else{System.out.println("转账失败!");}} catch (SQLException e) {try {//事务回滚connection.rollback();} catch (SQLException ex) {ex.printStackTrace();}e.printStackTrace();}finally {JDBCUtils.close(connection,preparedStatement1);JDBCUtils.close(null,preparedStatement2);}}
以上代码,感兴趣慢慢研究,如果去掉了事务管理,会怎么样呢?
数据库连接池
概念
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。
剖析
Java官方提供了DataSource接口,该接口由数据库厂商实现,用于构建数据库连接池,可以使用其方法来过去连接对象或者归还连接
C3P0数据库连接池
使用步骤
- 导入jar包
- 编写配置文件(注:文件必须是c3p0.properties或c3p0-config.xml)
- 通过
ComboPooledDataSource
创建DataSource对象 - 获取连接Connection对象
- 编写sql语句,执行并处理结果
- 归还连接对象
配置文件
<c3p0-config><default-config><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost/mybatis</property><property name="user">root</property><property name="password">mysql</property><!-- 初始化申请的连接数量 --><property name="initialPoolSize">10</property><!-- 最大的连接数量 --><property name="maxPoolSize">10</property><!-- 超时时间 --><property name="checkoutTimeout">3000</property></default-config><!-- This app is massive! --><named-config name="intergalactoApp"><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost/mybatis</property><property name="user">root</property><property name="password">mysql</property><!-- 初始化申请的连接数量 --><property name="initialPoolSize">10</property><!-- 最大的连接数量 --><property name="maxPoolSize">10</property><!-- 超时时间 --><property name="checkoutTimeout">3000</property></named-config>
</c3p0-config>
public static void main(String[] args) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {//1.导入jar包//2.编写配置文件//3.通过ComboPooledDataSource创建DataSource对象DataSource dataSource = new ComboPooledDataSource();//4.获取连接对象connection = dataSource.getConnection();//5.编写sql语句,执行并处理结果String sql = "select * from tbl_account";preparedStatement = connection.prepareStatement(sql);resultSet = preparedStatement.executeQuery();while (resultSet.next()) {int id = resultSet.getInt("id");String name = resultSet.getString("name");double balance = resultSet.getDouble("balance");System.out.println(id + " : " + name + " : " + balance);}} catch (SQLException e) {e.printStackTrace();} finally {if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if (preparedStatement != null) {try {preparedStatement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null) {try {//6.归还连接对象connection.close();} catch (SQLException e) {e.printStackTrace();}}}}
Druid数据库连接池
使用步骤
- 导入jar包
- 编写配置文件(注:不同于c3p0,该配置文件可自定义名称及位置,通过properties来读取它)
- 通过
DruidDataSourceFactory
创建DataSource对象 - 获取连接Connection对象
- 编写sql语句,执行并处理结果
- 归还连接对象
配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=mysql
maxActive=10
maxWait=3000
public class JDBCUtils {private static DataSource dataSource = null;static {try {Properties properties = new Properties();properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("conf/druid.properties"));dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}public static DataSource getDataSource(){return dataSource;}public static Connection getConnection() throws SQLException {return dataSource.getConnection();}public static void close(Connection connection, Statement statement) {if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(connection!=null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}public static void close(Connection connection, Statement statement, ResultSet resultSet){close(connection,statement);if(resultSet!=null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}
}
Spring JDBC
概念
Spring框架对JDBC的简单封装,简化JDBC的开发
常用方法
- 创建
JdbcTemplate
的对象,参数为一个数据库连接池DataSource
对象
JdbcTemplate jdbcTemplate = new JdbcTemplate(JDBCUtils.getDataSource());
- 使用
update
方法来实现增删改,返回影响的行数
String sql = "update tbl_account set balance = ? where id = ?";int count = jdbcTemplate.update(sql, balance, id);
String sql = "insert into tbl_account values(null,?,?)";int count = jdbcTemplate.update(sql, name, balance);
String sql = "delete from tbl_account where id = ?";int count = jdbcTemplate.update(sql, id);
- 使用
queryForMap
方法来返回数据,注:该查询的数据集的长度只能是1,列名作为key,值为value
String sql = "select * from tbl_account where balance = ?";Map<String, Object> map = jdbcTemplate.queryForMap(sql,balance);
- 使用
queryForList
方法来返回数据集,注:该方式是先将每条数据封装为Map集合,再装载到List集合中
String sql = "select * from tbl_account";List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
- 使用
queryForObject
方法来返回指定数值,注:该方式一般用于聚合函数的查询
String sql = "select count(*) from tbl_account";Long total = jdbcTemplate.queryForObject(sql, long.class);
- 使用
query
方法来返回指定对象的数值
**方式一:**自己写RowMapper接口的方法
**方式二:**使用BeanPropertyRowMapper实现类
String sql = "select * from tbl_account";/*List<Account> list = jdbcTemplate.query(sql,new RowMapper<Account>(){@Overridepublic Account mapRow(ResultSet resultSet, int i) throws SQLException {Account account = new Account();int id = resultSet.getInt("id");String name = resultSet.getString("name");double balance = resultSet.getDouble("balance");account.setId(id);account.setName(name);account.setBalance(balance);return account;}});*/List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class));
JavaWeb基础 - 跟JDBC说拜拜!
大家好,我是一只学弱狗,记录学习的点点滴滴!
优质文章
- 一张黄图的故事
- JavaSE练习项目
- 我是菜鸟、我小试牛刀
- linux指令太多记不住?小白看这篇就够了!
优质专栏
- 数据库就该这样学
- 爪哇外步篇
谨以此篇博客,结束时长整8个月的寒暑假生活。
畅聊JDBC
- JDBC
- 概念
- 使用步骤
- 代码演示
- 相关对象详解
- 疑问猜测
- DriverManager
- 概念
- 功能
- 注册驱动
- 获取数据库连接
- Connection
- 概念
- 功能
- 获取执行sql的对象
- 管理事务
- Statement
- 概念
- 常用方法
- ResultSet
- 概念
- 常用方法
- 案例:登陆验证功能
- 案例思考
- 事务管理及演示
- 什么是事务?
- 模拟:转账
- 数据库连接池
- 概念
- 剖析
- C3P0数据库连接池
- 使用步骤
- Druid数据库连接池
- 使用步骤
- Spring JDBC
- 概念
- 常用方法
JDBC
概念
JDBC,是Java Database Connectivity的缩写,即Java数据库连接,先看搜狗百科上的解释:JDBC是一种用于执行SQL语句的Java API,由一组用Java语言编写的类和接口组成,它可以为多种关系型数据库提供统一访问,据此可以构建更高级的工具和接口,实现了所有这些面向标准的目标并且具有简单,严格类型定义且高性能实现的接口。
上面的解释我勉强接受,用简单的语言我们这样来描述:它是Sun公司定义的一套操作所有关系型数据库的规则,各个数据库厂商,像MySQL、SQL Server等等,他们自己去实现这些接口,提供数据库的驱动jar包,我们可以按照Sun公司的规则(接口)编程。
使用步骤
- 导入驱动jar包(或者使用maven构建)
- 注册驱动
- 获取数据库连接对象
- 定义sql语句
- 获取执行sql语句的对象
- 执行sql语句,接收返回结果
- 处理结果
- 释放资源
Maven依赖
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.37</version>
</dependency>
代码演示
public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;//1.导入jar包try {//2.注册驱动Class.forName("com.mysql.jdbc.Driver");//3.获取连接对象connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "mysql");//4.定义sql语句String sql = "select * from tbl_user";//5.获取执行sql语句的对象statement = connection.createStatement();//6.执行sql语句resultSet = statement.executeQuery(sql);//7.处理结果while (resultSet.next()) {String username = resultSet.getString("username");String password = resultSet.getString("password");System.out.println(username + " : " + password);}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();} finally {//8.释放资源if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}}
相关对象详解
疑问猜测
看过了上面的代码,有小伙伴不禁会问,注册驱动,不就是通过反射加载Driver类进内存么,怎么就注册驱动了啊?
其实刚开始,我也有这样的疑问,不妨打开源码看一下
噢,原来这儿是一个静态代码块,懂了懂了。
DriverManager
概念
驱动管理对象
功能
注册驱动
在Driver的静态代码块中,我们发现,通过DriverManager的静态方法registerDriver来注册的驱动
获取数据库连接
public static Connection getConnection(String url,String user,String password)
- url:指定连接路径
- user:用户名
- password:密码
Connection
概念
数据库连接对象
功能
获取执行sql的对象
Statement createStatement() //执行静态sql
PreparedStatement prepareStatement(String sql) //执行预编译sql
管理事务
//开启事务
void setAutoCommit(boolean autoCommit) //调用该方法设置参数为false,即开启事务
//提交事务
void commit()
//回滚事务
void rollback()
Statement
概念
用于执行静态sql语句并返回其生成结果的对象
常用方法
int executeUpdate(String sql) //执行增删改语句,返回影响的行数
ResultSet executeQuery(String sql) //执行查语句,返回ResultSet结果集对象
ResultSet
概念
结果集对象,封装查询结果
常用方法
boolean next() //游标向下移动一行,判断当前行是否是数据行,如果是数据行则返回true,否则返回false
XXX getXXX() //获取数据,该方法为重载方法,可以传入列数,也可以传入列名
案例:登陆验证功能
任何理论都离不开实践!
需求:用户在控制台输入用户名和密码,核对其正确性,整个过程存储为日志信息。
public static void main(String[] args) {//待输入用户名String username = null;//待输入密码String password = null;//获取键盘输入流对象Scanner in = new Scanner(System.in);//日期对象Date date = new Date();//日期格式化对象SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");//缓冲字符输出流BufferedWriter bw = null;//数据库连接对象Connection connection = null;//执行sql语句对象Statement statement = null;//结果集对象ResultSet resultSet = null;try {//获取类加载器ClassLoader classLoader = Demo.class.getClassLoader();//获取指定资源的URL路径URL resource = classLoader.getResource("conf/log.txt");//获取指定资源的绝对路径String path = resource.getPath();//文件字符输出流 追加字符FileWriter fw = new FileWriter(path, true);bw = new BufferedWriter(fw);System.out.println("用户名:");username = in.nextLine();System.out.println("密码:");password = in.nextLine();connection = JDBCUtils.getConnection();String sql = "select * from tbl_user where username = '" + username + "' and password = '" + password + "' ";System.out.println(sql);statement = connection.createStatement();resultSet = statement.executeQuery(sql);String format = sdf.format(date);bw.newLine();bw.write("时间:" + format);bw.newLine();bw.write("用户名:" + username);bw.newLine();bw.write("密码:" + password);bw.newLine();if (resultSet.next()) {System.out.println("登录成功!");bw.write("登录成功!");} else {System.out.println("登录失败!");bw.write("登录失败!");}bw.newLine();bw.write("--------------------------------------");} catch (FileNotFoundException e) {System.out.println("未发现指定文件");System.exit(-1);} catch (SQLException e) {System.out.println("获取statement对象异常");System.exit(-1);} catch (IOException e) {System.out.println("IO读写异常");System.exit(-1);} finally {JDBCUtils.close(connection, statement, resultSet);try {bw.close();} catch (IOException e) {e.printStackTrace();}}}
注意到,有个JDBCUtils类,我们看下这个类
public class JDBCUtils {private static String driver = null;private static String url = null;private static String user = null;private static String password = null;static {try {Properties properties = new Properties();/*** 获取src路径下的文件的方式-->ClassLoader 类加载器*/properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("conf/jdbc.properties"));driver = properties.getProperty("driver");url = properties.getProperty("url");user = properties.getProperty("user");password = properties.getProperty("password");Class.forName(driver);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}public static Connection getConnection() {try {Connection connection = DriverManager.getConnection(url,user,password);return connection;} catch (SQLException e) {e.printStackTrace();return null;}}public static void close(Connection connection, Statement statement) {if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(connection!=null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}public static void close(Connection connection, Statement statement, ResultSet resultSet){close(connection,statement);if(resultSet!=null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}
}
仔细观察,提高了代码的复用性,通过配置文件修改数据库的属性,是不是值得我们学习呢?
案例思考
上面的代码,还是很不错的,但是也面临着一个问题,sql注入问题
纳尼?不可思议,观察sql语句,对于聪明的你我就不做更多的解释了?如何解决?PreparedStatement,之前我们说过,它是一个执行预编译sql的对象,如何操作呢?
首先原先的步骤得先发生下改变
- 导入驱动jar包
- 注册驱动
- 获取数据库连接对象 Connection
- 定义sql语句,注:sql的参数使用?来代替
- 获取执行sql语句的对象PreparedStatement
- 给占位符?赋值
- 执行sql语句,接收返回结果
- 处理结果
- 释放资源
于是,我们有个加强版
public static void main(String[] args) {String username = null;String password = null;Scanner in = new Scanner(System.in);Date date = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");BufferedWriter bw = null;Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {ClassLoader classLoader = DemoPlus.class.getClassLoader();URL resource = classLoader.getResource("conf/log.txt");String path = resource.getPath();FileWriter fw = new FileWriter(path, true);bw = new BufferedWriter(fw);/* FileOutputStream fos = new FileOutputStream(path,true);OutputStreamWriter osw = new OutputStreamWriter(fos);BufferedWriter bw = new BufferedWriter(osw);*/System.out.println("用户名:");username = in.nextLine();System.out.println("密码:");password = in.nextLine();connection = JDBCUtils.getConnection();String sql = "select * from tbl_user where username = ? and password = ?";preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1,username);preparedStatement.setString(2,password);resultSet = preparedStatement.executeQuery();String format = sdf.format(date);bw.newLine();bw.write("时间:" + format);bw.newLine();bw.write("用户名:" + username);bw.newLine();bw.write("密码:" + password);bw.newLine();if (resultSet.next()) {System.out.println("登录成功!");bw.write("登录成功!");} else {System.out.println("登录失败!");bw.write("登录失败!");}bw.newLine();bw.write("--------------------------------------");} catch (FileNotFoundException e) {System.out.println("未发现指定文件");System.exit(-1);} catch (SQLException e) {System.out.println("获取statement对象异常");System.exit(-1);} catch (IOException e) {System.out.println("IO读写异常");System.exit(-1);} finally {JDBCUtils.close(connection, preparedStatement, resultSet);try {bw.close();} catch (IOException e) {e.printStackTrace();}}}
我们再来sql注入一次,没门。。。
事务管理及演示
什么是事务?
指一个包含多个步骤的业务操作,如果这个业务被事务管理,则这多个步骤要么同时执行成功,要么同时失败。
模拟:转账
构建数据库表
我们的需求是张三给李四转1000元,期望结果张三余额是4000元,李四余额是6000元。
public static void main(String[] args) {//获取数据库连接对象Connection connection = JDBCUtils.getConnection();try {//开启事务connection.setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}//定义sql语句String sql = "update tbl_account set balance = balance + ? where id = ? ";//获取执行预编译sql语句的PreparedStatement对象PreparedStatement preparedStatement1 = null;PreparedStatement preparedStatement2 = null;try {preparedStatement1 = connection.prepareStatement(sql);preparedStatement1.setDouble(1,-1000);preparedStatement1.setInt(2,1);preparedStatement2 = connection.prepareStatement(sql);preparedStatement2.setDouble(1,+1000);preparedStatement2.setInt(2,2);int count1 = preparedStatement1.executeUpdate();//此处发生错误int a = 3/0;int count2 = preparedStatement2.executeUpdate();//提交事务connection.commit();if(count1>0 && count2>0){System.out.println("转账成功!");}else{System.out.println("转账失败!");}} catch (SQLException e) {try {//事务回滚connection.rollback();} catch (SQLException ex) {ex.printStackTrace();}e.printStackTrace();}finally {JDBCUtils.close(connection,preparedStatement1);JDBCUtils.close(null,preparedStatement2);}}
以上代码,感兴趣慢慢研究,如果去掉了事务管理,会怎么样呢?
数据库连接池
概念
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。
剖析
Java官方提供了DataSource接口,该接口由数据库厂商实现,用于构建数据库连接池,可以使用其方法来过去连接对象或者归还连接
C3P0数据库连接池
使用步骤
- 导入jar包
- 编写配置文件(注:文件必须是c3p0.properties或c3p0-config.xml)
- 通过
ComboPooledDataSource
创建DataSource对象 - 获取连接Connection对象
- 编写sql语句,执行并处理结果
- 归还连接对象
配置文件
<c3p0-config><default-config><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost/mybatis</property><property name="user">root</property><property name="password">mysql</property><!-- 初始化申请的连接数量 --><property name="initialPoolSize">10</property><!-- 最大的连接数量 --><property name="maxPoolSize">10</property><!-- 超时时间 --><property name="checkoutTimeout">3000</property></default-config><!-- This app is massive! --><named-config name="intergalactoApp"><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost/mybatis</property><property name="user">root</property><property name="password">mysql</property><!-- 初始化申请的连接数量 --><property name="initialPoolSize">10</property><!-- 最大的连接数量 --><property name="maxPoolSize">10</property><!-- 超时时间 --><property name="checkoutTimeout">3000</property></named-config>
</c3p0-config>
public static void main(String[] args) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {//1.导入jar包//2.编写配置文件//3.通过ComboPooledDataSource创建DataSource对象DataSource dataSource = new ComboPooledDataSource();//4.获取连接对象connection = dataSource.getConnection();//5.编写sql语句,执行并处理结果String sql = "select * from tbl_account";preparedStatement = connection.prepareStatement(sql);resultSet = preparedStatement.executeQuery();while (resultSet.next()) {int id = resultSet.getInt("id");String name = resultSet.getString("name");double balance = resultSet.getDouble("balance");System.out.println(id + " : " + name + " : " + balance);}} catch (SQLException e) {e.printStackTrace();} finally {if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if (preparedStatement != null) {try {preparedStatement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null) {try {//6.归还连接对象connection.close();} catch (SQLException e) {e.printStackTrace();}}}}
Druid数据库连接池
使用步骤
- 导入jar包
- 编写配置文件(注:不同于c3p0,该配置文件可自定义名称及位置,通过properties来读取它)
- 通过
DruidDataSourceFactory
创建DataSource对象 - 获取连接Connection对象
- 编写sql语句,执行并处理结果
- 归还连接对象
配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=mysql
maxActive=10
maxWait=3000
public class JDBCUtils {private static DataSource dataSource = null;static {try {Properties properties = new Properties();properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("conf/druid.properties"));dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}public static DataSource getDataSource(){return dataSource;}public static Connection getConnection() throws SQLException {return dataSource.getConnection();}public static void close(Connection connection, Statement statement) {if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(connection!=null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}public static void close(Connection connection, Statement statement, ResultSet resultSet){close(connection,statement);if(resultSet!=null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}
}
Spring JDBC
概念
Spring框架对JDBC的简单封装,简化JDBC的开发
常用方法
- 创建
JdbcTemplate
的对象,参数为一个数据库连接池DataSource
对象
JdbcTemplate jdbcTemplate = new JdbcTemplate(JDBCUtils.getDataSource());
- 使用
update
方法来实现增删改,返回影响的行数
String sql = "update tbl_account set balance = ? where id = ?";int count = jdbcTemplate.update(sql, balance, id);
String sql = "insert into tbl_account values(null,?,?)";int count = jdbcTemplate.update(sql, name, balance);
String sql = "delete from tbl_account where id = ?";int count = jdbcTemplate.update(sql, id);
- 使用
queryForMap
方法来返回数据,注:该查询的数据集的长度只能是1,列名作为key,值为value
String sql = "select * from tbl_account where balance = ?";Map<String, Object> map = jdbcTemplate.queryForMap(sql,balance);
- 使用
queryForList
方法来返回数据集,注:该方式是先将每条数据封装为Map集合,再装载到List集合中
String sql = "select * from tbl_account";List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
- 使用
queryForObject
方法来返回指定数值,注:该方式一般用于聚合函数的查询
String sql = "select count(*) from tbl_account";Long total = jdbcTemplate.queryForObject(sql, long.class);
- 使用
query
方法来返回指定对象的数值
**方式一:**自己写RowMapper接口的方法
**方式二:**使用BeanPropertyRowMapper实现类
String sql = "select * from tbl_account";/*List<Account> list = jdbcTemplate.query(sql,new RowMapper<Account>(){@Overridepublic Account mapRow(ResultSet resultSet, int i) throws SQLException {Account account = new Account();int id = resultSet.getInt("id");String name = resultSet.getString("name");double balance = resultSet.getDouble("balance");account.setId(id);account.setName(name);account.setBalance(balance);return account;}});*/List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class));
发布评论