查询account 表中内容
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 查询jt_db库中account表中的所有记录
*/
public class test1 {
public static void main(String[] args) throws Exception {
//1.注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8",
"root", "root");
//3.获取传输器
Statement stat = conn.createStatement();
//4.基于传输器发送SQL语句到服务器执行,返回执行结果
String sql = "select * from account";
ResultSet rs = stat.executeQuery(sql);
//5.处理结果(打印到控制台)
while( rs.next() ) {
int id = rs.getInt( "id" );
String name = rs.getString( "name" );
double money = rs.getDouble( "money" );
System.out.println( id+","+name+","+money );
}
//6.释放资源
rs.close();
stat.close();
conn.close();
}
}
进行异常处理后的代码
package jdbc;
import java.io.Closeable;
import java.sql.*;
import java.util.Arrays;
/**
* 查询jt_db库中account表中的所有记录
*/
public class TestJdbc02 {
public static void main(String[] args) {
//将变量的声明提升到main函数的最上面
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//1.注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接
conn = DriverManager.getConnection(
"jdbc:mysql:///jt_db?characterEncoding=utf-8",
"root", "root");
//3.获取传输器
stat = conn.createStatement();
//4.基于传输器发送SQL语句到服务器执行,返回执行结果
String sql = "select * from account";
rs = stat.executeQuery(sql);
//5.处理结果(打印到控制台)
while( rs.next() ) {
int id = rs.getInt( "id" );
String name = rs.getString( "name" );
double money = rs.getDouble( "money" );
System.out.println( id+","+name+","+money );
}
} catch (Exception e) {
System.out.println( "查询失败!" );
e.printStackTrace();
} finally { //finally中的代码一定会执行!
//6.释放资源
if( rs != null ) { //反之,如果rs为null,直接跳过,不需要关闭!
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs = null;
}
}
if( stat != null ) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
stat = null;
}
}
if( conn != null ) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
}
增删改查
package jdbc;
import org.junit.Test;
import util.JdbcUtil;
import java.sql.*;
public class TestJdbc03 {
/* 1、新增:往account表中添加一个名称为john、money为3500的记录 */
@Test
public void testAdd() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//注册驱动并获取链接
Class.forName( "com.mysql.jdbc.Driver" );
conn = DriverManager.getConnection(
"jdbc:mysql:///jt_db?characterEncoding=utf-8",
"root", "root" );
//获取传输器,并执行sql,返回执行结果
stat = conn.createStatement();
String sql = "insert into account value(null, 'john', 3500)";
int rows = stat.executeUpdate( sql );
//处理结果
System.out.println( "影响行数: "+rows );
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
JdbcUtil.close(conn, stat, rs);
}
}
/* 2、修改:将account表中名称为john的记录,money修改为1500 */
@Test
public void testUpdate() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//注册驱动并获取连接
conn = JdbcUtil.getConn();
//获取传输器并执行sql语句,返回执行结果
stat = conn.createStatement();
String sql = "update account set money=1500 where name='john'";
int rows = stat.executeUpdate( sql );
System.out.println( "影响行数: "+rows );
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
JdbcUtil.close(conn, stat, rs);
}
}
/* 3、删除:删除account表中名称为john的记录 */
@Test
public void testDelete() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//注册驱动并获取连接
conn = JdbcUtil.getConn();
//获取传输器并执行sql语句,返回执行结果
stat = conn.createStatement();
String sql = "delete from account where name='john'";
int rows = stat.executeUpdate( sql );
System.out.println( "影响行数: "+rows );
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, stat, rs);
}
}
/* 4、查询:查询account表中id为1的记录 */
@Test
public void testFindById() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConn();
stat = conn.createStatement();
String sql = "select * from account where id=1";
rs = stat.executeQuery( sql );
if( rs.next() ) {
int id = rs.getInt("id");
String name = rs.getString( "name" );
double money = rs.getDouble( "money" );
System.out.println( id+":"+name+":"+money );
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, stat, rs);
}
}
}
为了提高代码复用性,封装 JdbcUtil 工具,包括 getcon()函数和close()函数
JdbcUtil 工具类
package util;
import java.sql.*;
public class JdbcUtil {
/**
* 注册驱动并获取连接对象(默认连接的库是jt_db,用户名密码都是root)
* @return 连接对象
*/
public static Connection getConn(){
Connection conn = null;
try {
Class.forName( "com.mysql.jdbc.Driver" );
conn = DriverManager.getConnection(
"jdbc:mysql:///jt_db?characterEncoding=utf-8",
"root", "root" );
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 注册驱动并获取连接对象
* @param db 连接的数据库的名字
* @param user 连接的数据库的用户名
* @param pwd 连接的数据库的密码
* @return 连接对象
*/
public static Connection getConn( String db, String user, String pwd ){
Connection conn = null;
try {
Class.forName( "com.mysql.jdbc.Driver" );
conn = DriverManager.getConnection(
"jdbc:mysql:///"+db+"?characterEncoding=utf-8",
user, pwd );
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 释放JDBC程序中的资源
* @param conn 连接对象
* @param stat 传输器对象
* @param rs 结果集对象
*/
public static void close(Connection conn, Statement stat, ResultSet rs) {
if( rs != null ) { //反之,如果rs为null,直接跳过,不需要关闭!
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs = null;
}
}
if( stat != null ) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
stat = null;
}
}
if( conn != null ) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
模拟用户登录
(1)准备数据
use jt_db;
create table user(
id int primary key auto_increment,
username varchar(50),
password varchar(50)
);
insert into user values(null,'张三','123');
insert into user values(null,'李四','234');
(2)创建LoginUser 类,提供 main 方法 和 login 方法
package ps;
import org.junit.Test;
import util.JdbcUtil;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
/**
* 模拟用户登录案例
*/
public class LoginUser {
public static void main(String[] args) {
Scanner sc = new Scanner( System.in );
//1.提示用户登录
System.out.println( "请登录:" );
//2.提示用户输入用户名并接收用户名
System.out.println( "请输入用户名:" );
String user = sc.nextLine();
//3.提示用户输入密码并接收密码
System.out.println( "请输入密码:" );
String pwd = sc.nextLine();
//4.调用login方法,将用户名和密码传入,进行登录
login( user, pwd );
}
/**
* 根据用户名和密码查询jt_db.user表中的数据
* @param user 用户名
* @param pwd 密码
*/
private static void login(String user, String pwd) {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//注册驱动并获取连接
conn = JdbcUtil.getConn();
//获取传输器,执行sql,返回执行结果
stat = conn.createStatement();
String sql = "select * from user where username='"+user
+"' and password='"+pwd+"'";
System.out.println( sql );
rs = stat.executeQuery( sql );
//处理结果
if( rs.next() ) { //true,表明有数据,则用户名密码正确,允许登录
System.out.println( "恭喜您登录成功!" );
}else { //false,没有数据,则用户名后密码不正确,登录失败
System.out.println( "登录失败! 用户名或密码不正确! " );
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
JdbcUtil.close(conn, stat, rs);
}
}
}
执行时,输入:
请登录:
请输入用户名:
张飞’#’
请输入密码:
select * from user where username=‘张飞’#’’ and password=’’
恭喜您登录成功了!
或输入:
请登录:
请输入用户名:
张飞’ or '1=1
请输入密码:
select * from user where username=‘张飞’ or ‘1=1’ and password=’’
恭喜您登录成功了!
SQL注入攻击
通过上面的案例,我们发现在执行时,不输入密码只输入用户名也可以登陆成功。这就是SQL注入攻击。
SQL注入攻击产生的原因: 由于后台执行的SQL语句是拼接而来的:
select * from user where username='"+user+"' and password='"+pwd+"'
其中的参数是用户提交过来的,如果用户在提交参数时,在参数中掺杂了一些SQL关键字(比如or)或者特殊符号(#、-- 、’ 等),就可能会导致SQL语句语义的变化,从而执行一些意外的操作(用户名或密码不正确也能登录成功)
防止SQL注入攻击
如何防止SQL注入攻击?
(1) 使用正则表达式对用户提交的参数进行校验。如果参数中有(# – ’ or等)这些符号就直接结束程序,通知用户输入的参数不合法
(2) 使用PreparedStatement对象来替代Statement对象。下面通过第二种方式解决SQL注入攻击:添加loginByPreparedSatement方法,在方法中,使用PreparedStatement来代替Statement作为传输器对象使用,代码示例:
package ps;
import org.junit.Test;
import util.JdbcUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Scanner;
/**
* 模拟用户登录案例
*/
public class LoginUser2 {
public static void main(String[] args) {
Scanner sc = new Scanner( System.in );
//1.提示用户登录
System.out.println( "请登录:" );
//2.提示用户输入用户名并接收用户名
System.out.println( "请输入用户名:" );
String user = sc.nextLine();
//3.提示用户输入密码并接收密码
System.out.println( "请输入密码:" );
String pwd = sc.nextLine();
//4.调用login方法,将用户名和密码传入,进行登录
login( user, pwd );
}
/**
* 根据用户名和密码查询jt_db.user表中的数据
* @param user 用户名
* @param pwd 密码
*/
private static void login(String user, String pwd) {
Connection conn = null;
//Statement stat = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//注册驱动并获取连接
conn = JdbcUtil.getConn();
//获取传输器,执行sql,返回执行结果
String sql = "select * from user where username=? and password=?";
//将sql(骨架)传给服务器编译并确定下来(一旦编译之后,就不能再被改变)
ps = conn.prepareStatement( sql );
//传参数
ps.setString( 1, user );
ps.setString( 2, pwd );
//执行sql语句
rs = ps.executeQuery( );//不要再次传入sql语句,上面已经传过了
//处理结果
if( rs.next() ) { //true,表明有数据,则用户名密码正确,允许登录
System.out.println( "恭喜您登录成功!" );
}else { //false,没有数据,则用户名后密码不正确,登录失败
System.out.println( "登录失败! 用户名或密码不正确! " );
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
JdbcUtil.close(conn, ps, rs);
}
}
/** 使用PreparedStatement传输器查询所有用户 */
@Test
public void testFindAll() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConn();
String sql = "select * from user";
ps = conn.prepareStatement( sql );
rs = ps.executeQuery(); //不要再传SQL语句
while( rs.next() ) {
int id = rs.getInt( "id" );
String username = rs.getString( "username" );
String password = rs.getString( "password" );
System.out.println( id+":"+username+":"+password );
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, ps, rs);
}
}
}
再次执行程序,按照上面的操作登录。
此时,已经成功的防止了SQL注入攻击问题了。
PreparedStatement对象是如何防止SQL注入攻击的:使用PreparedStatement对象是先将SQL语句的骨架发送给服务器编译并确定下来,编译之后SQL语句的骨架和语义就不会再被改变了,再将SQL语句中的参数发送给服务器,即使参数中再包含SQL关键字或者特殊符号,也不会导致SQL语句的骨架或语义被改变,只会被当作普通的文本来处理!
数据库连接池
什么是连接池?
池:指内存中的一片空间(容器,比如数组、集合)连接池:就是将连接存放在容器中,供整个程序共享,可以实现连接的复用,减少连接创建和关闭的次数,从而提高程序执行的效率!
为什么要使用连接池?
1、传统方式操作数据库
Connection conn = DriverManager.getConnection( url, user, pwd );
//创建连接对象
conn.close(); //关闭连接, 销毁连接
在传统方式中,每次用户需要连接访问数据库时,都是创建一个连接对象,基于这个连接对象访问数据库,用完连接后,会将连接关闭(conn.close)
由于每次创建连接和关闭连接非常的耗时间而且耗资源,因此会导致程序执行的效率低下
2、使用连接池操作数据库
可以在程序一启动时,就创建一批连接放在一个连接池中(容器),当用户需要连接时,就从连接池中获取一个连接对象,用完连接后,不要关闭,而是将连接再还回连接池中,这样一来,用来用去都是池中的这一批连接,实现了连接的复用,减少了连接创建和关闭的次数,从而提高了程序执行的效率
如何使用C3P0连接池
使用C3P0连接池开发步骤:
1、导入开发包
2、创建数据库连接池(对象)
ComboPooledDataSource cpds = new ComboPooledDataSource();
3、设置连接数据库的基本信息
(1)方式一:(不推荐) 直接将参数通过 pool.setXxx方法设置给c3p0程序这种方式直接将参数写死在了程序中,后期一旦参数发生变化,就要修改程序,要重新编译项目、重新发布项目,非常麻烦。
(2)方式二:将连接参数提取到properties文件中(推荐)
文件必须放在src(源码根目录)目录下 !
文件名必须叫做 c3p0.properties !
在类目录下(开发时可以放在src或者类似的源码目录下),添加一个c3p0.properties文件,配置内容如下:
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///jt_db?characterEncoding=utf-8
c3p0.user=root
c3p0.password=root
这种方式由于是c3p0到指定的位置下寻找指定名称的properties文件,所以文件的位置必须是放在src或其他源码根目录下,文件名必须是c3p0.properties
(3)方式三:(推荐)文件必须放在src(源码根目录)目录下 !
文件名必须叫做 c3p0-config.xml
在类目录下(开发时可以放在src或者类似的源码目录下),添加一个c3p0-config.xml文件,配置内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property
name="jdbcUrl">jdbc:mysql:///jt_db?characterEncoding=utf-8</property>
<property name="user">root</property>
<property name="password">root</property>
</default-config>
</c3p0-config>
这种方式由于是c3p0到指定的位置下寻找指定名称的xml文件,所以文件的位置必须是放在src或其他源码根目录下,文件名必须是c3p0-config.xml
4、从连接池中获取一个连接对象并进行使用
Connection conn = pool.getConnection();
5、用完连接后将连接还回连接池中
JdbcUtil.close(conn, ps, rs);
/* 如果是自己创建的连接对象,这个连接对象没有经过任何的改动,调用
* conn.close方法,是将连接对象关闭
* 如果是从连接池中获取的连接对象,该连接对象在返回时就已经被连接池
* 改造了,将连接对象的close方法改为了还连接到连接池中
*/
package c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import util.JdbcUtil;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class TestC3P0 {
/* 使用c3p0查询account表中的所有数据 */
@Test
public void testFindAll(){
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
//获取一个连接池对象(获取存放连接的容器)
ComboPooledDataSource pool = new ComboPooledDataSource();
try {
//这只连接数据库的基本信息(Driver/url/user/password)
//c3p0程序会到类目录下寻找c3p0.properties文件或者c3p0-config.xml文件
//从连接池中获取一个连接对象,进行使用
// connection = JdbcUtil.getConnection();
connection = pool.getConnection();
statement = connection.createStatement();
String sql = "select * from account";
resultSet = statement.executeQuery(sql);
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("name");
double money = resultSet.getDouble("money");
System.out.println(id+":"+username+":"+money);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection,statement,resultSet);
}
}
}
c3p0.properties
# key = value
c3p0.driverClass = com.mysql.jdbc.Driver
c3p0.jdbcUrl = jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8
c3p0.user = root
c3p0.password = root
c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///jt_db?characterEncoding=utf-8</property>
<property name="user">root</property>
<property name="password">root</property>
</default-config>
</c3p0-config>