JavaWeb之JDBC(数据库连接)

来源:互联网 发布:便宜好用剃须刀知乎 编辑:程序博客网 时间:2024/05/21 06:47

持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。

大多数情况下,特别是企业级应用,

数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,

而持久化的实现过程大多通过各种关系数据库来完成。

持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。


java中的数据存储技术:

在Java中,数据库存取技术可分为如下几类:
1、JDBC直接访问数据库
2、JDO技术
3、第三方O/R工具,如Hibernate, ibatis 等

JDBC的重要性:
JDBC是java访问数据库的基石,JDO, Hibernate等只是更好的封装了JDBC。

JDBC简介:

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,使用这个类库可以以一种标准的方法、方便地访问数据库资源
JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统

这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。


在过去的时候,在连接每一个数据库系统的时候,都要写一个应用程序,去实现连接,代码得不到重用。每一次数据库都要重写连接程序。


有了JDBC之后:


JDBC体系结构:多态

JDBC接口(API)包括两个层次:
 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。


JDBC驱动程序分类

JDBC驱动程序:各个数据库厂商根据JDBC的规范制作的 JDBC 实现类的类库 
JDBC驱动程序总共有四种类型:
第一类:JDBC-ODBC桥。 
第二类:部分本地API部分Java的驱动程序。 
第三类:JDBC网络纯Java驱动程序。 
第四类:本地协议的纯 Java 驱动程序。 
第三、四两类都是纯Java的驱动程序,因此,对于Java开发者来说,它们在性能、可移植性、功能等方面都有优势。 


本地协议的纯 Java 驱动程序

多数数据库厂商已经支持允许客户程序通过网络直接与数据库通信的网络协议
这种类型的驱动程序完全使用 Java 编写,通过与数据库建立的 Socket 连接,采用具体与厂商的网络协议把 JDBC 调用转换为直接连接的网络调用



JDBC API

JDBC API 是一系列的接口,它使得应用程序能够进行数据库联接,执行SQL语句,并且得到返回结果。



statement简介:

/*通过JDBC想指定的数据表中插入一条记录
 * statement:用于执行sql语句对象
 * 通过Connection的CreateStatement()方法来获取

 * 通过executeUpdate(sql)可以执行sql语句
 * 传入的sql可以是insert、update、delete但是不能是select
 * connection、statement都是应用程序和数据库服务器的连接资源,使用后一定要关闭

 */

prepareStatement简介:

prepareStatement:是statement的子接口,可以传入占位符的sql语句,并且提供了补充占位符变量

的相关方法。

使用preparementstatement

创建preparestatement

String sql="intsert into examstudent values(?,?,?,?,?,?,?)

prepareStatement ps=connection.preparestatement(sql)

调用preparestatement的serxxx(int index,object  val 设置占位符的值

@Test
public void testPrepareStatement(){
Connection connection=null;
PreparedStatement preparedStatement=null;
try {
connection=JDBCTools.getconnection();
  String sql="INSERT INTO customers(customers_name,customers_email,customers_birth) VALUES(?,?,?)";
   preparedStatement =(PreparedStatement) connection.prepareStatement(sql);


   preparedStatement.setString(1, "atguigu");
   preparedStatement.setString(2, "@@@1223");
   preparedStatement.setDate(3, 
    new java.sql.Date(new Date().getTime()));


   preparedStatement.executeUpdate();


} catch (InstantiationException | IllegalAccessException
| ClassNotFoundException | SQLException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCTools.release(connection, preparedStatement, null);
}
}

通用的方法:

//insert,update,delete操作都可以包含在其中

                                                     //可变参数的好处
public void update(String sql,Object ... args){

Connection connection=null;
PreparedStatement preparedStatement=null;
try {
connection=JDBCTools.getconnection();
preparedStatement=(PreparedStatement) connection.prepareStatement(sql);
   for(int i=0;i<args.length;++i){
    preparedStatement.setObject(i+1, args[i]);
   }
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCTools.release(connection, preparedStatement, null);
}
}




/**
* 执行sql语句,使用preparedstatement

* @param sql
* @param args:填写sql占位符的可变参数
* @throws SQLException 
*/
                           //可变参数        
public static void update(String sql,Object ... args ) throws SQLException{
Connection connection=null;
PreparedStatement preparedStatement=null;
try {
connection=JDBCTools.getconnection();
 
 preparedStatement=(PreparedStatement) connection.prepareStatement(sql);
   for(int i=0;i<args.length;++i){
    preparedStatement.setObject(i+1, args[i]);
   }
   preparedStatement.executeUpdate();

} catch (InstantiationException | IllegalAccessException
| ClassNotFoundException | SQLException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCTools.release(connection, preparedStatement);
}

}


ResultSet简介:

结果集,封装了使用JDBC进行查询的结果

1、调用statement 对象的executeQuery(sql)可以得到结果集

2、resultset返回的实际上就是一张数据表,有一个指针指向数据表的第一样的前面

可以调用next()方法检测下一个是否有效,若该有效 该方法返回true且指针下移

相当于Iterator对象的hasnext()和next()方法的结合体

3、档指针对位到一行时,可以通过调用getxxx(index)或者getxxx(columname)

获取每一列的值,例如:getInt(1),getString("name")

4、ResultSet当然也需要进行关闭


ResultSet类实现的查询:

@Test
public void testResultSet() throws InstantiationException, IllegalAccessException, 
ClassNotFoundException, 
SQLException, IOException{
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
try {
connection=JDBCTools.getconnection();
statement=(Statement) connection.createStatement();
String sql="SELECT * FROM customers";

//执行查询,得到ResultSet
resultSet=statement.executeQuery(sql);
//处理resultset

                     
while(resultSet.next()){

int customers_id=resultSet.getInt(1);
String customers_name=resultSet.getString(2);
String customers_email=resultSet.getString(3);
Date custmers_birth=resultSet.getDate(4);
System.out.print(customers_id);
System.out.print(customers_name);
System.out.print(customers_email);
System.out.print(custmers_birth);
System.out.println();

}

} catch (Exception e) {

}
finally{
JDBCTools.release(connection, statement, resultSet);
}
}


第一阶段:获取数据库的connection:driver类、drivermanager类

Driver接口简介:

Java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。

这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现
在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些

一、Driver类实现连接

实现oracle和mysql的connection:

package com.atguigu.jdbc;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import oracle.jdbc.driver.OracleDriver;
import org.junit.Test;
import com.mysql.jdbc.Driver;
public class JDBCTest {
@Test
public void testDriver() throws SQLException{

//这种连接方式,将连接需要的信息写死了。不能达到数据库的连接的重用。
Driver driver=new Driver();
String url="jdbc:mysql://localhost:3306/bookstore";
java.util.Properties info=new java.util.Properties();
info.put("user","root");
info.put("passwor","");
Connection connection= driver.connect(url, info);
System.out.println(connection);
}
/*编写一个通用的方法,在不改变源程序的情况下,
 * 可以获取任何数据库的连接。
 * 解决方法:把数据库驱动Driver实现累的全类名
 * url、user、password放入一个配置一个文件中
 * 通过修改配置文件的方式实现和具体的数据库解耦
 */

public Connection getconnection() throws 
InstantiationException, IllegalAccessException, 
ClassNotFoundException, SQLException, IOException{
String driverClass=null;
String jdbcUrl=null;
String user=null;
String password=null;

 //将连接需要的信息放在一个文件中
//读取jdbc.properties文件

InputStream in=getClass()
.getClassLoader()
.getResourceAsStream("jdbc.properties");
Properties properties=new Properties();
properties.load(in);


driverClass=properties.getProperty("driverClass");
jdbcUrl=properties.getProperty("jdbcUrl");
user=properties.getProperty("user");
password=properties.getProperty("password");

//每一次连接时,这个是会变的。
oracle.jdbc.driver.OracleDriver driver=
(OracleDriver) Class.forName(driverClass).newInstance();


    Properties info=new Properties();
    info.put("user", user);
    info.put("password", password);
    
    Connection connection=driver.connect(jdbcUrl, info);
    return connection;
}
@Test
public void testGetConnection() throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, IOException{
System.out.println(getconnection());
}
}


二、DriverManager是管理驱动类,

通过DriverManager的getConnection()方法获取数据库连接

@Test
public void TestDriverManager() throws IOException, ClassNotFoundException, SQLException{
String driverClass=null;
String jdbcUrl=null;
String user=null;
String password=null;
//读取jdbc.properties文件
InputStream in=getClass()
.getClassLoader()
.getResourceAsStream("jdbc.properties");
Properties properties=new Properties();
properties.load(in);
driverClass=properties.getProperty("driverClass");
jdbcUrl=properties.getProperty("jdbcUrl");
user=properties.getProperty("user");
password=properties.getProperty("password");
        //加载数据库驱动程序

Class.forName(driverClass);
Connection connection=(Connection) DriverManager.getConnection(jdbcUrl, user, password);
System.out.println(connection);

}

关于jdbc.properties文件:

#driverClass=com.mysql.jdbc.Driver
#jdbcUrl=jdbc:mysql://localhost:3306/bookstore
#user=root
#password=root


driverClass=oracle.jdbc.driver.OracleDriver
jdbcUrl=jdbc:oracle:thin:@localhost:1521:oracle
user=scott
password=tiger

全部的注释: ctrl+t 全部撤销注释也是这个


加载和注册JDBC驱动:

加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名
DriverManager 类是驱动程序管理器类,负责管理驱动程序
通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实例,因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例


建立连接:

可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接
JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
JDBC URL的标准由三部分组成,各部分间用冒号分隔。 
jdbc:<子协议>:<子名称>
协议:JDBC URL中的协议总是jdbc 
子协议:子协议用于标识一个数据库驱动程序
子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息 



几种常见数据库的JDBC url:

对于 Oracle 数据库连接,采用如下形式: 
jdbc:oracle:thin:@localhost:1521:sid
对于 SQLServer 数据库连接,采用如下形式:
jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=sid
对于 MYSQL 数据库连接,采用如下形式:   
jdbc:mysql://localhost:3306/sid

第二阶段:获取数据库连接后,对数据库中的数据进行操作。

一流的公司做的是标准,面向接口编程。


面向对象编程

1、项数据表插入一条student记录

2、先一个方法:void addStudent(Student student)

把参数student对象插入到数据库中

新建一个student,对应examstudents数据表

int flowId,int type,String idCard,String examcard,String studentName

string studetnname ,string location,int grade;


package com.atguigu.jdbc.reviewtest;


import static org.junit.Assert.*;


import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;


import org.junit.Test;


import com.atguigu.jdbc.tools.JDBCTools;
import com.mysql.jdbc.Statement;
import com.sun.corba.se.spi.orbutil.fsm.Guard.Result;


/**
 * @author Administrator
 *
 */
public class ReviewTest {



案例一、//最顶层的设计:按照准考证查询或者按着身份证号查询
@Test
public void testGetStudent(){
//1、得到查询的类型
int searchType=getSearchTypeFromConsole();
     //2、具体查询学生的信息
Student student=searchStudent(searchType);
//3、打印学生的信息
printStudent(student);
}





private void printStudent(Student student) {
/*打印学生信息:若学生存在则打印其具体信息,
* 如不存在:打印查无此人
*/
if(student!=null){
System.out.println(student);
}else{
System.out.println("查无此人!");
}
}
private Student searchStudent(int searchType) {

//2、根据searchType确定sql
//3、执行查询
//4、若存在查询结果,把查询结果封装为一个Student对象
String sql="SELECT * FROM examstudents where ";
Scanner scanner=new Scanner(System.in);
//1、根据输入的searchType,提示用户输入信息
 //1.若searchType为1,提示输入身份证号
 //2、如searchType为2,提示输入准考证号
if(searchType==1){
System.out.println("请输入准考证号:");
String examCard=scanner.next();
sql=sql+"exam_Card='"+examCard+"'";
}else{
System.out.println("输入身份证号:");
String idCard=scanner.next();
sql=sql+"id_Card='"+idCard+"'";
}
//3、执行查询
Student student=getStudent(sql);
return student;
}


private Student getStudent(String sql) {
Student student=null;
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
try {
connection=JDBCTools.getconnection();
statement=(Statement) connection.createStatement();
resultSet=statement.executeQuery(sql);
if(resultSet.next()){
student=new Student(resultSet.getInt(1),resultSet.getInt(2), 
resultSet.getString(3),resultSet.getString(4),
resultSet.getString(5), resultSet.getString(6),
resultSet.getInt(7));
}
} catch (Exception e) {
// TODO: handle exception
}finally{
JDBCTools.release(connection, statement, resultSet);
}
return student;
}
private int getSearchTypeFromConsole() {
/*从控制台读入一个整数,确定要查询的类型
 * 1、用身份证查询
 * 2、用准考证查询其他无效,
 * 并提示重新输入
 */
System.out.println("请输入查询类型:"
+ "1、用准考证查询 2、用身份证查询。");
Scanner scanner=new Scanner(System.in);
int type=scanner.nextInt();
if(type!=1&&type!=2){
//中止程序
System.out.println("输入错误请重新输入!");
throw new RuntimeException();
}
return type;
}

案例二、//从控制台把数据输入到数据库中
@Test
public void testAddNewStudent() throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, IOException{
Student student=getStudentFromConsole();
//面向对象编程
addNewStudent(student);
}

private Student getStudentFromConsole() {
Scanner scanner=new Scanner(System.in);
Student student=new Student();

System.out.println("FlowId:");
student.setFlowId(scanner.nextInt());

System.out.println("Type:");
student.setType(scanner.nextInt());

System.out.println("IdCard:");
student.setIdCard(scanner.next());

System.out.println("ExamCard:");
student.setExamCard(scanner.next());

System.out.println("studentname:");
student.setStudentname(scanner.next());

System.out.println("location:");
student.setLocation(scanner.next());;

System.out.println("grade:");
student.setGrade(scanner.nextInt());
return student;
}
public void addNewStudent(Student student) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, IOException{

//这条语句拼的实在恶心
//面向对象编程
String sql="insert into examstudents"
+" values("+student.getFlowId()
+",'"+student.getType()
+"','"+student.getIdCard()
+"','"+student.getExamCard()
+"','"+student.getStudentname()
+"','"+student.getLocation()
+"',"+student.getGrade()+")";
System.out.println(sql);
JDBCTools.testUpdate(sql);
}
}


工具类:

package com.atguigu.jdbc.tools;


import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;


import com.mysql.jdbc.Driver;
import com.mysql.jdbc.Statement;


public class JDBCTools {
    //获取连接的方法
public static Connection getconnection() throws 
InstantiationException, IllegalAccessException, 
ClassNotFoundException, SQLException, IOException{
String driverClass=null;
String jdbcUrl=null;
String user=null;
String password=null;
//读取jdbc.properties文件
InputStream in=JDBCTools.class.getClassLoader()
.getResourceAsStream("jdbc.properties");
Properties properties=new Properties();
properties.load(in);
driverClass=properties.getProperty("driverClass");
jdbcUrl=properties.getProperty("jdbcUrl");
user=properties.getProperty("user");
password=properties.getProperty("password");
Driver driver=
(Driver) Class.forName(driverClass).newInstance();

   Properties info=new Properties();
   info.put("user", user);
   info.put("password", password);
   
   Connection connection=driver.connect(jdbcUrl, info);
   return connection;
}
    //关闭资源的方法
public static void release(Connection connection,Statement statement) throws SQLException{

try {
if(statement!=null)
statement.close();
} catch (Exception e) {
// TODO: handle exception
}finally{
if(connection!=null){
connection.close();
}
}
}
    public static void release(Connection connection,Statement statement,ResultSet resultSet){
    if(resultSet!=null)
    {
    try {
resultSet.close();
} catch (Exception e) {
// TODO: handle exception
}
    }
    if(statement!=null){
    try {
statement.close();
} catch (Exception e) {
// TODO: handle exception
}
    }
    if(connection!=null){
    try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
    }
   
    }
    public static void testUpdate(String sql) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, IOException{
    Connection conn=null;
    Statement statement=null;
    try {
    conn = JDBCTools.getconnection();
       statement=(Statement) conn.createStatement();
       statement.execute(sql);
       
    } catch (Exception e) {
    // TODO: handle exception
    }
    finally{
    release(conn,statement);
    }
    }
}


sql注入攻击:

一、sql注入是利用某些熊没用对用户输入的数据进行重分的检查,而在用户数据

注入非法的sql语句段或命名,从而利用系统的sql引擎完成恶意行为的做法

二、对于java而言,要防止sql,只要用preparedstatement取代statement就可以了


statement和preparedstatement的对比:

@Test
public void testSQLInjection2(){
   

String username="a' OR userpassword=";
String userpassword=" OR '1'='1";
String sql="select * from user where username=? and userpassword=?";
System.out.println(sql);
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
connection=JDBCTools.getconnection();
   preparedStatement=(PreparedStatement) connection.prepareStatement(sql);
   preparedStatement.setString(1, username);
   preparedStatement.setString(2, userpassword);
   
   resultSet=preparedStatement.executeQuery();
   if(resultSet.next()){
    System.out.println("登录成功");
   }else{
    System.out.println("登录失败");
   }
} catch (Exception e) {
// TODO: handle exception
}finally{
JDBCTools.release(connection, preparedStatement, resultSet);
}

}
@Test
public void testSQLInjection(){
String username="a' OR userpassword=";
String userpassword=" OR '1'='1";
String sql="select * from user where username= '"
+username+"' and "
+"userpassword= '"+userpassword+"'";
System.out.println(sql);
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
try {
connection=JDBCTools.getconnection();
statement=(Statement) connection.createStatement();
   resultSet=statement.executeQuery(sql);
   if(resultSet.next()){
    System.out.println("登录成功");
   }else{
    System.out.println("登录失败");
   }
} catch (Exception e) {
// TODO: handle exception
}finally{
JDBCTools.release(connection, statement, resultSet);
}
}


代码的可读性和可维护性. 
PreparedStatement 能最大可能提高性能:
DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次.  
(语法检查,语义检查,翻译成二进制命令,缓存)
PreparedStatement 可以防止 SQL 注入 











0 0
原创粉丝点击