Jdbc核心
- Jdbc介绍
- Jdbc增删改查使用步骤
- Jdbc中核心API
- SQL注入
- PreparedStatement使用
- Jdbc通用封装
1、Jdbc介绍
1>什么是jdbc
Java Database Connectivity
使用Java代码访问数据库的一种技术解决方案,Jdbc中定义了一套标准接口,即访问数据库的通用API,不同的数据库厂商必须去实现这些接口,以此可以通过相同的方式来访问不同的数据库,实现与具体数据库无关的Java操作
2>jdbc的工作原理
1)Jdbc技术标准定义一系列接口
在接口中定义访问数据库以及对数据表进行操作的抽象方法
2)各大数据库厂商自己写接口的实现类
将接口的实现类封装成驱动包提供给程序开发人员使用
3)程序员导入驱动包,访问数据时调用JDBC定义的接口
根据多态,实际底层调用的是数据库厂商的实现类
2、Jdbc增删改查使用步骤
- 创建项目,导入数据库驱动包
- 加载驱动
Class.forName("com.mysql.jdbc.Driver"); - 创建连接
Connection conn =
DriverManager.getConnection(url,username,password); - 创建Statement对象
Statement stmt = conn.createStatement(); - 发送并执行sql语句
查询: Result rs = stmt.executeQuery(sql);
增删改:int rows = stmt.executeUpdate(sql); - 释放资源
rs.close();
stmt.close();
conn.close();
package com.lanou.jdbc;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Jdbc访问数据基本操作演示,
* 查询lanou库下myemp表中的所有数据
*/
public class JdbcDemo01 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1、加载驱动程序到内存中
Class.forName("com.mysql.jdbc.Driver");
//2、创建与数据库之间的连接(url,username,password)
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/lanou", "root","1234");
System.out.println(conn);
//3、构建给数据库发送sql语句的对象
Statement stmt = conn.createStatement();
//4、发送sql语句,并将查询接口封装到ResultSet中
ResultSet rs = stmt.executeQuery("select * from myemp");
//5、处理结果集
// 结果集行指针,默认指向第一行上面
//next() 判断有没有下一行,有自动挪到下一行,并取该行数据
while(rs.next()) {
//通过指定列名,获取该列下数据
int id = rs.getInt("id");
String name = rs.getString("name");
double salary = rs.getDouble("salary");
Date hiredate = rs.getDate("hiredate");
//System.out.println(id+" "+name+" "+salary+" "+hiredate);
//通过指定列顺序来获取列下面的值(列顺序从1开始)
id = rs.getInt(1);
name = rs.getString(2);
salary = rs.getDouble(5);
hiredate = rs.getDate(6);
System.out.println(id+":"+name+":"+salary+":"+hiredate);
}
//6、关闭资源(后打开的先关闭)
if(rs!=null){
rs.close();
}
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
}
package com.lanou.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/**
* Jdbc访问数据基本操作演示,
* 对lanou库下dept表的增删改操作
*/
public class JdbcDemo02 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
while(true){
System.out.println("请输入指令:1>插入,2>删除,3>修改");
int type = new Scanner(System.in).nextInt();
switch (type) {
case 1:
add();
break;
case 2:
delete();//根据部门编号删除
break;
case 3:
update();//根据部门编号修改
break;
}
}
}
/**插入数据*/
public static void add() throws ClassNotFoundException, SQLException{
System.out.println("输入部门编号:");
int deptno = new Scanner(System.in).nextInt();
System.out.println("输入部门名称:");
String dname = new Scanner(System.in).nextLine();
System.out.println("输入部门地址:");
String location = new Scanner(System.in).nextLine();
//sql语句换行注意空格
String sql = "INSERT INTO dept "
+ "VALUES("+deptno+",'"+dname+"','"+location+"')";
System.out.println(sql);
executeSql(sql);
}
/**修改数据*/
public static void update() throws ClassNotFoundException, SQLException{
System.out.println("输入要修改的部门编号:");
int deptno = new Scanner(System.in).nextInt();
System.out.println("输入要修改的部门名称:");
String dname = new Scanner(System.in).nextLine();
System.out.println("输入要修改的部门地址:");
String location = new Scanner(System.in).nextLine();
//修改的sql
String sql = "update dept set dname='"+dname+"',"
+ "location='"+location+"' where deptno="+deptno;
System.out.println(sql);
executeSql(sql);
}
/**删除数据
* @throws Exception */
private static void delete() throws ClassNotFoundException, SQLException {
System.out.println("输入要删除的部门编号:");
int deptno = new Scanner(System.in).nextInt();
//删除sql
String sql = "delete from dept where deptno="+deptno;
System.out.println(sql);
executeSql(sql);
}
/**用于执行增删改sql语句的方法*/
public static void executeSql(String sql) throws ClassNotFoundException, SQLException {
//1、加载驱动程序到内存中
Class.forName("com.mysql.jdbc.Driver");
//2、创建与数据库之间的连接(url,username,password)
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/lanou", "root","1234");
System.out.println(conn);
//3、构建给数据库发送sql语句的对象
Statement stmt = conn.createStatement();
//4、执行语句,返回受影响行数
int rows = stmt.executeUpdate(sql);
if(rows!=0) {
System.out.println("数据操作成功");
}
}
}
3、Jdbc中核心API
主要API:(JDBC定义的接口标准)
- DriverManager(C):管理驱动程序,并通过驱动程序建立与数据库的连接
- Connection(I):代表与数据库之间建立起来的连接对象
- Statement(I):发送sql语句给数据库
execute(sql) 适合所有sql语句,返回boolean,代表有无结果集
executeQuery(sql) 适合select查询语句,返回查询结构集
executeUpdate(sql) 适合delete,update,add,返回影响行数 - ResultSet(I):代表查询的结果集
rs.next() 移动指针到下一行数据
rs.getXX() 获取数据
1)必须先调用rs.next()挪动指针后才能获取当前行数据
2)参数为整数,根据结果列列数获取当前列下数据,列数从1开始
3)参数为字符串,根据列名来获取当前列下数据
4)所有类型都可以使用getString()来获取
4、SQL注入演示
所谓SQL注入,就是通过把SQL命令插入到Web[表单]提交或输入域名或请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句
1、创建一张user表如下
2、代码
package com.lanou.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/**
* SQL注入演示
*/
public class JdbcDemo03 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1、加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2、创建连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/lanou",
"root","1234");
//3、创建Statement
Statement stmt = conn.createStatement();
//4、执行sql语句
//登录:根据账号和密码查询信息
System.out.println("===用户登录===");
System.out.println("请输入账号:");
String username = new Scanner(System.in).nextLine();
System.out.println("请输入密码:");
String password = new Scanner(System.in).nextLine();
//这里可以通过拼接字符串的方式来造成sql注入
String sql = "select * from user "
+ "where username='"+username+"' and password='"+password+"'";
System.out.println(sql);
//5、执行sql
ResultSet rs = stmt.executeQuery(sql);
if(rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//6、释放资源
if(rs!=null){
rs.close();
}
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
}
5、PreparedStatement应用
- PreparedStatement继承了Statement接口
- 此对象允许在sql语句中以?设置占位符
- PreparedStatement和Statment区别
1>Statement是拼接sql字符串,可能会造成sql注入
PreparedStatement是先编译sql语句,然后在传入参数值
不会造成sql注入
2>Statement每一次都要传入的sql语句编译一次,效率较低
PreparedStatement在preparedStatement(sql)时就已经将
sql语句传给数据库,并编译好为执行做准备,后面传入参数
然后执行SQL语句,所以效率较高
3>尽量使用PreparedStatement
package com.lanou.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
public class JdbcDemo04 {
public static void main(String[] args) throws Exception {
System.out.println("请输入指令:1>登录2>注册");
int type = new Scanner(System.in).nextInt();
switch (type) {
case 1:
login();
break;
case 2:
regist();
}
}
/**用户登录*/
public static void login() throws ClassNotFoundException, SQLException {
//1、加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2、创建连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/lanou1811",
"root","1234");
//3、预编译sql语句
// ?充当占位符
String sql = "select * from user "
+ "where username=? and password=?";
//会将sql语句发送给数据库,但不会立即执行
PreparedStatement pstmt =
conn.prepareStatement(sql);
//给占位符赋值,sql语句从左到右,从1开始
System.out.println("===用户登录===");
System.out.println("请输入账号:");
String username = new Scanner(System.in).nextLine();
System.out.println("请输入密码:");
String password = new Scanner(System.in).nextLine();
pstmt.setString(1,username);
pstmt.setString(2,password);
System.out.println(sql);
//4、执行sql语句
ResultSet rs = pstmt.executeQuery();
if(rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//5、释放资源
if(rs!=null){
rs.close();
}
if(pstmt!=null){
pstmt.close();
}
if(conn!=null){
pstmt.close();
}
}
/**用户注入*/
private static void regist() throws Exception{
//1、加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2、创建连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/lanou1811",
"root","1234");
//3、预编译sql语句
System.out.println("===用户注册===");
//判断账号是否和已有账号重复,如果重复了重新输入
//判断用户名是否存在
boolean isUserNameExists = true;
String username;
do{
System.out.println("请输入账号:");
username = new Scanner(System.in).nextLine();
//判断账号是否为空
if("".equals(username)||username==null){
System.out.println("账号不能为空");
continue;
}
String sql = "select * from user where "
+ " username=?";
PreparedStatement pstmt =
conn.prepareStatement(sql);
pstmt.setString(1,username);
//执行sql
ResultSet rs = pstmt.executeQuery();
if(rs.next()){//账号已存在
isUserNameExists = true;
System.out.println("账号已存在,请重新输入");
}else{//账号不存在
isUserNameExists = false;
System.out.println("恭喜,账号可以使用");
}
}while(isUserNameExists);
//判断密码是否为空
boolean isPwdNull = false;
String password = null;
do{
System.out.println("请输入密码:");
password = new Scanner(System.in).nextLine();
if("".equals(password)||password==null){
System.out.println("密码不能为空,重新输入");
isPwdNull = true;
}else{
isPwdNull = false;
}
}while(isPwdNull);
//预编译插入的sql语句
String sql = "insert into user(username,password)"
+ " values(?,?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,username);
pstmt.setString(2,password);
//4、执行sql语句,返回受影响的行数
int rows = pstmt.executeUpdate();
if(rows!=0){
System.out.println("数据插入成功");
}else{
System.out.println("数据插入失败");
}
//5、释放资源
if(pstmt!=null){
pstmt.close();
}
if(conn!=null){
pstmt.close();
}
}
}
6、Jdbc通用封装
1>配置文件
properties配置文件,便于修改
#jdbc.properties文件配置
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/lanou1811
jdbc.username=root
jdbc.password=1234
empDao=jdbc.EmpDaoImpl
#deptDao=jdbc.DeptDaoImpl
2>读取并加载配置信息
/**
* 读取properties配置文件
*/
public class Config{
static Properties pro = new Properties();
static{
loadPro();
}
public static void loadPro(){
//从类路径下加载配置文件
InputStream in = Config.class.getClassLoader().
getResourceAsStream("jdbc.properties");
try {
pro.load(in);
} catch (IOException e) {
e.printStackTrace();
} finally{
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 根据key获取value值
* @param key
* @return
*/
public String getValue(String key){
return pro.getProperty(key);
}
}
/**存储全局常量*/
public class GlobalConst {
static Config config = new Config();
//url
public static final String URL =
config.getValue("url");
//driver
public static final String DRIVER =
config.getValue("driverClassName");
//username
public static final String USERNAME =
config.getValue("username");
//password
public static final String PASSWORD =
config.getValue("password");
}
3>jdbc工具类
主要封装connection连接对象的获取和关闭操作
通过ThreadLocal保证一个线程一个连接对象
/**工具类*/
public class JdbcUtil extends GlobalConst{
/**
* 封装获取连接的方法
* @return
* @throws ClassNotFoundException
* @throws SQLException
* 一个线程一个连接对象
*
* ThreadLocal:可以将一个对象绑定到当前线程
*/
static ThreadLocal<Connection> td =
new ThreadLocal<>();
public static Connection getConnection() throws ClassNotFoundException, SQLException{
//判断当前线程有没有绑定连接对象
//如果有直接返回,
//如果没有则创建一个连接对象并绑定到当前线程
Connection conn = td.get();
if(conn==null){
//1、加载驱动
Class.forName(DRIVER);
//2、创建连接
conn = DriverManager.getConnection(
URL,USERNAME,PASSWORD);
//3、将连接对象绑定到当前线程
td.set(conn);
}
return conn;
}
/**
* 释放资源
* connection
* statement/preparedStatement
* resultset
* */
public static void release(Connection conn,
Statement stmt,ResultSet rs){
try {
if(rs!=null)rs.close();
if(stmt!=null)stmt.close();
if(conn!=null){
//将conn对象从当前线程移除
td.remove();
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 对增删改三个方法封装
* @param sql
* @param parameter 给占位符参数
* @return DML操作受影响行数
*/
public static int update(String sql,Object[] parameters){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JdbcUtil.getConnection();
pstmt = conn.prepareStatement(sql);
//给占位符赋值
for(int i=0;i<parameters.length;i++){
pstmt.setObject(i+1,parameters[i]);
}
return pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally{
JdbcUtil.release(conn,pstmt,null);
}
return 0;
}
}
4>实体类
便于对数据库中数据的封装
ORM:Object Relation Mapping 描述对象和数据之间映射
将java程序中对象对应到数据库中表
-表和类的对应
-表中字段和类中属性对应
-记录和对象对应
package com.lanou.jdbc;
import java.io.Serializable;
import java.sql.Date;
/**
* 数据库中每有一张表都应该对应java中一个实体类
* 表的字段对应实体类的属性
* 每一行记录对应实体类一个对象
* ORM:Object Relation Mapping
* 对象关系映射
*/
public class Emp implements Serializable{
private int id;
private String name;
private String sex;
private String job;
private double salary;
private Date hiredate;
private int deptno;
public Emp() {
super();
}
public Emp(int id, String name, String sex, String job, double salary, Date hiredate, int deptno) {
super();
this.id = id;
this.name = name;
this.sex = sex;
this.job = job;
this.salary = salary;
this.hiredate = hiredate;
this.deptno = deptno;
}
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 String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
@Override
public String toString() {
return "Emp [id=" + id + ", name=" + name + ", sex=" + sex + ", job=" + job + ", salary=" + salary
+ ", hiredate=" + hiredate + ", deptno=" + deptno + "]";
}
}
5>dao
Data Access Object 数据访问对象
数据访问对象,封装对数据库的访问操作
建立在业务层和数据库之间,封装所有对数据库的访问操作
目的:数据访问逻辑和业务逻辑分开
/**
* DAO:Data Access Object
* 数据访问对象
* 该类专门用于封装访问数据库的操作
* 降低程序与数据库之间的耦合度
*/
public interface EmpDao {
/**查询所有数据*/
public List<Emp> findAll();
/**根据id查询某一条记录*/
public Emp findEmpById(int id);
/**根据id删除某一条记录*/
public int deleteEmpById(int id);
/**插入一条记录*/
public int addEmp(Emp emp);
/**修改一条记录*/
public int updateEmp(Emp emp);
}
package com.lanou.jdbc;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
*EmpDao接口实现类
*/
public class EmpDaoImpl implements EmpDao{
@Override
public List<Emp> findAll() {
List<Emp> emps = new ArrayList<>();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
String sql = "select * from myemp";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
//将每一行记录封装成一个emp对象,然后存入list集合
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String sex = rs.getString("sex");
String job = rs.getString("job");
double salary = rs.getDouble("salary");
Date hiredate = rs.getDate("hiredate");
int deptno = rs.getInt("deptno");
Emp emp = new Emp(
id, name, sex, job, salary,
hiredate, deptno);
emps.add(emp);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally{
JdbcUtil.release(conn, pstmt, rs);
}
return emps;
}
@Override
public Emp findEmpById(int id) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Emp emp = null;
try {
conn = JdbcUtil.getConnection();
pstmt = conn.prepareStatement(
"select * from myemp where id=?");
pstmt.setInt(1,id);
rs = pstmt.executeQuery();
if(rs.next()){
emp = new Emp();
emp.setId(id);
emp.setName(rs.getString("name"));
emp.setSex(rs.getString("sex"));
emp.setJob(rs.getString("job"));
emp.setSalary(rs.getDouble("salary"));
emp.setHiredate(rs.getDate("hiredate"));
emp.setDeptno(rs.getInt("deptno"));
}
} catch (Exception e) {
} finally {
JdbcUtil.release(conn, pstmt, rs);
}
return emp;
}
/**
*String[] array;
*array = {1,2,3};
*
*array = new String[]{1,2,3}
*/
@Override
public int deleteEmpById(int id) {
return update(
"delete from myemp where id=?",
new Object[]{id});
}
@Override
public int addEmp(Emp emp) {
return update(
"insert into myemp(name,sex,job,"
+ "salary,hiredate,deptno)"
+ "values(?,?,?,?,?,?)",
new Object[]{
emp.getName(),
emp.getSex(),
emp.getJob(),
emp.getSalary(),
emp.getHiredate(),
emp.getDeptno()
});
}
@Override
public int updateEmp(Emp emp) {
return update(
"update myemp set "
+ "name=?,"
+ "sex=?,"
+ "job=?,"
+ "salary=?,"
+ "hiredate=?,"
+ "deptno=?"
+ "where id=?",
new Object[]{
emp.getName(),
emp.getSex(),
emp.getJob(),
emp.getSalary(),
emp.getHiredate(),
emp.getDeptno(),
emp.getId()
});
}
/**
* 对增删改三个方法封装
* @param sql
* @param parameter 给占位符参数
* @return DML操作受影响行数
*/
public int update(String sql,Object[] parameters){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JdbcUtil.getConnection();
pstmt = conn.prepareStatement(sql);
//给占位符赋值
for(int i=0;i<parameters.length;i++){
pstmt.setObject(i+1,parameters[i]);
}
return pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally{
JdbcUtil.release(conn,pstmt,null);
}
return 0;
}
}
6>工厂模式dao
package com.lanou.jdbc;
/**
* 用于生产dao对象的工厂
*/
public class DaoFactory {
/**
* empDao deptDao
* @param daoName
* @return
* @throws ClassNotFoundException
*使用反射封装
*/
public static Class<?> getDao(String daoName) throws ClassNotFoundException{
Config config = new Config();
String daoClassName = config.getValue(daoName);
//根据类的全限定名获取类对象
Class<?> clz = Class.forName(daoClassName);
return clz;
}
/**不用反射封装*/
/*public static EmpDao getEmpDao(){
return new EmpDaoImpl();
}
public static DeptDao getDeptDao(){
return new DeptDaoImpl();
}*/
}
7>测试
package com.lanou.jdbc;
import java.util.List;
/**测试jdbc封装*/
public class Test6 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
//获取dao对象
EmpDao dao = (EmpDao) DaoFactory.getDao("empDao").newInstance();
List<Emp> emps = dao.findAll();
for(Emp emp:emps){
System.out.println(emp);
}
// Emp emp = dao.findEmpById(3);
// System.out.println(emp);
// int rows = dao.deleteEmpById(11);
// System.out.println(rows);
}
}