JDBC(java database connectivity)
面向接口开发思想
- java数据库连接
- java语言和数据库之间的链接(java提供的api),为多种数据库提供统一的访问
- 每个数据库内部的操作方式都不一样,我们不可能自己写实现类操作数据库,因为这是数据库公司的内部机密,所以每个数据库厂商都提供一套操作自己数据库的实现类给我们调用
- 链接mysql需要用到mysql公司提供的mysql-connector-java-5.1.39-bin.jar文件(数据库供应商写好的)
- java提供数据库操作的接口,供应商根据接口来进行实现操作数据库,这样,通过同一套接口,可以实现不同数据库的操作,而不需要不同数据库需要调用不同的api
- 需要加载驱动,驱动程序中的类会实现JDBC的接口,我们只需要学习接口就好(驱动就是接口的实现类)
JDBC开发步骤
- 注册驱动:加载厂商实现jdbc接口的实现类
- 通过drivermanager(驱动管理者),调用registerDriver(Driver)
- Driver驱动接口类,对象在导入的驱动中
- 传入mysql驱动程序中的Driver对象
- 获取连接:链接到厂商的数据库
- 获得语句执行平台:获取操作数据库的实现类(sql语句的执行者)
- 执行sql语句:操作数据库
- 处理结果:得到结果
- 释放资源:断开连接
- 1,2,3,6都是固定的.只有4,5是开发人员可控的
- 因为加载的驱动是jar包,里面都是编译后的class文件,点进入相应的类中是没有源码的,这时需要自己导入源码,在下载官方的驱动包时有一个src文件夹,里面放的就是源码,我们attach resource the src floder,如果选择导入file,则源码需要是以jar的形式提供的
import com.mysql.jdbc.Driver;
public static void main(String[] args) throws SQLException {
// 注册驱动
DriverManager.registerDriver(new Driver());
}
- 在Driver类源码中,调用了一段静态代码块,注册一次驱动,只要一加载类,就会调用
- 如果按照上面的写法写,就会注册2次驱动,造成浪费
- 通过反射的方式把类加载进内存,这样就可以只调用一次加载驱动
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
}
- 通过 DriverManager获取链接,得到的链接对象是在驱动包中的实现类
//static Connection getConnection(String url, String user, String password)
//返回值是Connection接口的实现类,在mysql驱动程序
//url: 数据库地址 jdbc:mysql://连接主机IP:端口号//数据库名字
String url = "jdbc:mysql://localhost:3306/mybase";
//用户名和密码用自己的
String username="root";
String password="root";
Connection con = DriverManager.getConnection(url, username, password);
- 通过得到的连接(connection)对象,去获取数据库的操作对象Statement
- Statement制定int executeUpDate(string sql),仅限于执行insert,update,delete
- 通过SQLYog拼接sql语句
- 关闭资源
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JDBCDemo {
public static void main(String[] args) throws Exception {
//1.注册驱动 反射技术,将驱动类加入到内容
//使用java.sql.DriverManager类静态方法 registerDriver(Driver driver)
// Diver是一个接口,参数传递,MySQL驱动程序中的实现类
//DriverManager.registerDriver(new Driver());
//驱动类源代码,注册2次驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库连接 DriverManager类中静态方法
//static Connection getConnection(String url, String user, String password)
//返回值是Connection接口的实现类,在mysql驱动程序
//url: 数据库地址 jdbc:mysql://连接主机IP:端口号//数据库名字
String url = "jdbc:mysql://localhost:3306/mybase";
String user = "root";
String password = "root";
//3.获得语句执行平台, 通过数据库连接对象,获取到SQL语句的执行者对象
// con对象调用方法 Statement createStatement() 获取Statement对象,将SQL语句发送到数据库
// 返回值是 Statement接口的实现类对象,,在mysql驱动程序
Connection conn = DriverManager.getConnection(url,user,password);
Statement statement = conn.createStatement();
//4.执行sql语句
// 通过执行者对象调用方法执行SQL语句,获取结果
// int executeUpdate(String sql) 执行数据库中的SQL语句, insert delete update
// 返回值int,操作成功数据表多少行
int rows = statement.executeUpdate("INSERT INTO sort(sname,sprice,sdesc) VALUES('xx',50, 'xx');");
System.out.println(rows);
//6.释放资源 一堆close()
statement.close();
conn.close();
}
}
public class JDBCDemo1 {
public static void main(String[] args) throws Exception{
//1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接对象
String url = "jdbc:mysql://localhost:3296/mybase";
String username="root";
String password="123";
Connection con = DriverManager.getConnection(url, username, password);
//3 .获取执行SQL 语句对象
Statement stat = con.createStatement();
// 拼写查询的SQL
String sql = "SELECT * FROM sort";
//4. 调用执行者对象方法,执行SQL语句获取结果集
// ResultSet executeQuery(String sql) 执行SQL语句中的select查询
// 返回值ResultSet接口的实现类对象,实现类在mysql驱动中
ResultSet rs = stat.executeQuery(sql);
//5 .处理结果集
// ResultSet接口方法 boolean next() 返回true,有结果集,返回false没有结果集
while(rs.next()){
//获取每列数据,使用是ResultSet接口的方法 getXX方法参数中,建议写String列名
System.out.println(rs.getInt("sid")+" "+rs.getString("sname")+
" "+rs.getDouble("sprice")+" "+rs.getString("sdesc"));
}
rs.close();
stat.close();
con.close();
}
}
sql注入
- 登陆查询
- SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码
- 此时,当用户输入正确的账号与密码后,查询到了信息则让用户登录
- 但是当用户输入的账号为XXX 密码为:XXX’ OR ‘a’=’a时,则真正执行的代码变为
- SELECT * FROM 用户表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’ OR ’a’=’a’
- 此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题
select * from tbl_user where usr_name='xx' and usr_pwd='xx';
select * from tbl_user where usr_name='xx' and usr_pwd='xx or 1=1';
预编译sql语句
- PrepareStatement接口预编译SQL语句
# 使用PreparedStatement预处理对象时,建议每条sql语句所有的实际参数,都使用逗号分隔。
# String sql = "insert into sort(sid,sname) values(?,?)";;
# PreparedStatement预处理对象代码:
# PreparedStatement psmt = conn.prepareStatement(sql)
//执行SQL语句,数据表,查询用户名和密码,如果存在,登录成功,不存在登录失败
String sql = "SELECT * FROM users WHERE username=? AND PASSWORD=?";
//调用Connection接口的方法prepareStatement,获取PrepareStatement接口的实现类
//方法中参数,SQL语句中的参数全部采用问号占位符
PreparedStatement pst = con.prepareStatement(sql);
System.out.println(pst);
//调用pst对象set方法,设置问号占位符上的参数
pst.setObject(1, "username");
pst.setObject(2, "password");
//调用方法,执行SQL,获取结果集
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("username")+" "+rs.getString("password"));
}
rs.close();
pst.close();
con.close();
JDBCUtils(JSBC工具类)
- 简化代码,方便返回数据库链接对象Connection和关闭资源
public class JDBCUtils {
//私有构造方法
private JDBCUtils(){}
private static Connection con ;
//加载驱动
static{
try{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mybase";
String username="root";
String password="root";
con = DriverManager.getConnection(url, username, password);
}catch(Exception ex){
throw new RuntimeException(ex+"数据库连接失败");
}
}
//获取链接
public static Connection getConnection(){
return con;
}
//关闭资源
public static void close(Connection con,Statement stat){
if(stat!=null){
try{
stat.close();
}catch(SQLException ex){}
}
if(con!=null){
try{
con.close();
}catch(SQLException ex){}
}
}
//关闭资源
public static void close(Connection con,Statement stat , ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch(SQLException ex){}
}
close(conn,stat);
}
}
public class TestJDBCUtils {
public static void main(String[] args)throws Exception {
Connection con = JDBCUtils.getConnection();
PreparedStatement pst = con.prepareStatement("SELECT sname FROM sort");
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("sname"));
}
JDBCUtils.close(con, pst, rs);
}
}
properties配置文件
- DBUtils类通过读取配置文件去设定数据库地址,数据库用户名密码,驱动地址等信息
- 通常都存在配置文件中,方便后期维护,程序如果需要更换数据库,只需要修改配置文件即可
- property文件建议放在src目录下
- 假设现在项目开发完成了,并且测试完毕,要交付给用户了,我们给用户的是bin目录下的class文件,不可能把整个工程都给用户的(包含源码)
- 文件的扩展名为property
- 内容为key=value的形式
- 如果再好一点,会放在远程服务器上,让其读取配置
- property文件的编写
properties文件的编写(内容如下):
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3296/mybase
username=root
password=123
//获得当前类的类加载器(负责加载类的对象)
ClassLoader loader = 当前类.class.getClassLoader();
//获取文件的流对象
InputStrea, fis = loader.getRescoreAsStream(String path);//输入文件名
Properties pro = new Properties();
pro.load(fis);
public class PropertiesDemo {
public static void main(String[] args) throws Exception{
//使用类的加载器
InputStream in = PropertiesDemo.class.getClassLoader().getResourceAsStream("database.properties");
System.out.println(in);
Properties pro = new Properties();
pro.load(in);
//获取集合中的键值对
String driverClass=pro.getProperty("driverClass");
String url = pro.getProperty("url");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
Class.forName(driverClass);
Connection con = DriverManager.getConnection(url, username, password);
System.out.println(con);
}
}