jdbc开发步骤图

以下要用到的products表

一. JDBC简介

  补充    JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口,各个数据库厂商趋势线这个接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类(各个数据库,自己去实现jdbc这个接口)

  JDBC(java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的API,可以为多种关系数据库提供统一访问,它由一组用java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序

(1)Java DataBase Connectivity(java数据库连接)

(2)组成包:java.sql.*;javax.sql.*;这两个包都包含在了JDK中。

(3)还需要数据库的驱动,这些驱动就相当于对JDBC规范的实现

 主要接口或类

 1. DriverManger

作用:

  a. 注册驱动

  b. 获取与数据库的连接

改进注册驱动:

  DriverManager.registerDriver(new com.mysql.jdbc.Driver());

  缺点:严重依赖具体的驱动类(导入什么驱动的包就只能注册什么驱动),会导致驱动被注册2次。

获取与数据库的连接

  DriverManager.getConnection("jdbc:mysql://localhost:3306/ssm", "root", "hang");  //ssm为数据库名

2. Connection

  所有与数据库的交互作用都是基于连接的基础之上的,想要对数据库进行操作,首先要获取此对象,从连接对象中获取执行数据库的statement对象 

  Statement stmt = conn.createStatement():创建向数据库发送sql的statement对象

  Conm.preparedStatement(sql)  -------更常用,可进行预编译,防止sql注入

3. Statement

作用:向数据库发送并执行具体的SQL语句

常用的方法:

  (1)ResultSet executeQuery(String sql):只适合查询,返回的是查询的结果集

  (2)int executeUpdate(String sql):适合DML,或者没有返回结果集的DDL,返回的是影响的记录行数

  (3)boolean execute(String sql):执行任何的SQL语句,返回的不是成功与否。有结果集的返回true,没有返回false

 4. PreparedStatement

当再次查询相同的数据时,则直接在缓存中获取,不需要再和数据库进行交换,所以说效率高

 5. ResultSet

  作用:代表查询语句的查询结果集

二. 入门程序以及改造

 1. 开发步骤:

(1)创建一个java项目

(2)导入mysql的数据库驱动jar包

(3)注册驱动

(4)获取与数据库的连接

(5)得到代表发送和执行SQL语句的对象--------Statement

(6)执行语句

(7)释放占用的资源

public class TestDemo {
public static void main(String[] args) throws Exception {
// 注册驱动
DriverManager.registerDriver(new Driver());
// 建立连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_demo1", "root", "feng");
// 获取执行sql语句的对象
Statement st = conn.createStatement();
String sql = "select * from products";
// 执行 返回结果
ResultSet set = st.executeQuery(sql);
//遍历结果集
while (set.next()) {
//获取字段值 为name的数据
String name = set.getString("name");
System.out.println(name);
}
// 释放资源
set.close();
st.close();
conn.close();
}
}

这个程序存在几个问题:

2. 注册驱动时会出现两次注册,代码扩展性不好问题

由com.mysql.jdbc.Driver()的源码(如下)

mysql的Driver类中有一个静态代码块,静态代码块中已经通过DriverManager注册了这个驱动!

大家都知道静态代码快是在类加载器加载这个类的字节码文件的时候就已经执行了的,也就是说,在DriverManager.registerDriver(new com.mysql.jdbc.Driver())这段代码中,一旦new了这个驱动,这个驱动就已经被加载了!

所以如果DriverManager.registerDriver(new com.mysql.jdbc.Driver()),实际上Mysql的Driver会被加载两次,所以只要new com.mysql.jdbc.Driver();实际上就已经注册驱动了

但是我们能new com.mysql.jdbc.Driver()这样子来注册驱动吗?可以!但是还是不提倡,为什么呢?因为这样的话还有一个问题,这样写十分依赖Jar包,一旦jar包找不到,编译时期就会报错。所以在开发过程中通常写成:Class.forName(“com.mysql.jdbc.Driver”);

这样的话,通过类加载的方式来加载Driver类,照样能够执行static代码块中的注册驱动的方法。而且由于将Driver的位置写成了字符串的形式,对jar包的依赖就降低了,也易于使用配置文件加载这个类,所以下次如果换成连接Oracle或者其他数据库,改一下配置文件再填一个jar包就可以了。

使用Class.forName("com.mysql.jdbc.Driver")进行注册,为了代码的可扩展性,下面采取了读取配置文件的形式获取相应的参数(为了保障资源一定能被释放,应该放到finaly代码块中去)

public class JdbcDemo2 {
// 读取配置文件,获取连接需要的数据
static String url;
static String userName;
static String passWord;
static {
try {
Properties p = new Properties();
p.load(JdbcDemo2.class.getClassLoader().getResourceAsStream("db.properties"));
String className = p.getProperty("className");
url = p.getProperty("url");
userName = p.getProperty("userName");
passWord = p.getProperty("passWord");
// 注册驱动
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet set = null;
try {
// 建立连接
conn = DriverManager.getConnection(url, userName, passWord);
// 得到执行sql语句的对象
st = conn.createStatement();
String sql = "select * from products";
// 执行并返回结果
set = st.executeQuery(sql);
while(set.next()) {
String str = set.getString("price");
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 释放资源
try {
if (set != null) {
set.close();
set = null;
}
if (st != null) {
st.close();
st = null;
}
if (conn != null) {
conn.close();
conn = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

以上代码中出现的如st = null是为了让jvm更快的回收st对象,即使创建对象的引用指向为空。

3. 工具类的提取(将连接和资源的释放封装)

  根据上面的例子发现,获取数据库连接,释放数据库资源的代码都一样,可以提取出来一个方法,当获取连接,释放资源就直接调用方法,减少代码的冗余度。

public class JdbcUtils {
static String url;
static String userName;
static String passWord;
static {
Properties p = new Properties();
try {
p.load(JdbcUtils.class.getClassLoader().getResourceAsStream("properties"));
String className = p.getProperty("className");
url = p.getProperty("url");
userName = p.getProperty("userName");
passWord = p.getProperty("passWord");
// 注册
Class.forName(className); } catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接方法
public static Connection getConnection() {
Connection conn = null;
try {
conn = DriverManager.getConnection(url, userName, passWord);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
} // 释放资源
public static void release(ResultSet set, Connection conn,Statement st) {
try {
if (set != null) {
set.close();
set = null;
}
if (st != null) {
st.close();
st = null;
}
if (conn != null) {
conn.close();
conn = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

三  单元测试

作用:在普通的方法中执行一段代码

1.

下面代码是无法运行的

public class Test1 {
public void test1() {
System.out.println("哈哈");
}
}

但利用单元测试(Junit)可达到运行此段代码的目的,步骤:在test1方法上方加上一个@Test,并将光标放置Test上,点击提示的Junit添加至路径,即可运行

当点击run as时,你会发现出现Junit Test,点击即可运行,其一般配合@Before(被修饰的方法在@Test修饰的方法前运行)和@After使用(被修饰的方法在@Test修饰的方法后运行)

public class Test1 {
@Test
public void test1() {
System.out.println("哈哈");
} @Before
public void init() {
System.out.println("哈哈之前");
}
@After
public void after() {
System.out.println("哈哈之后");
}
}

运行结果:

 2. 将单元测试运用至jdbc

此例注意点,与数据库交互会产生乱码,解决----->在url处制定编码格式,如下(因为此处获取url是在配置文件中,所以在配置文件中修改编码)

public class TestJunit {
// 执行数据库的链接,并获取执行sql语句的对象
Connection conn = null;
Statement st = null;
ResultSet set = null;
@Before
public void getConnection() throws Exception {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
}
// 执行数据库的增删改查
@Test
public void test() throws SQLException {
//增
// st.execute("insert into products values(null ,'一个抢手的故事',10,'文艺',1,'一把锃亮的手枪')");
//删
// st.execute("delete from products where id = 11");
//改
// st.execute("update products set name='奋斗' where id =1");
// 查
ResultSet set = st.executeQuery("select * from products");
while(set.next()) {
System.out.println(set.getString(2));
}
} // 释放资源
@After
public void release() {
JdbcUtils.release(set, conn, st);
}
}

 四  SQL注入

SQL Injection:就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串中,最终达到欺骗服务器执行恶意的SQL命令。

具体来说,它是利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。

以下是会造成sql注入的代码:

public static void main(String[] args) throws Exception {

        DriverManager.registerDriver(new Driver());
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_demo1", "root", "feng");
// 获取执行sql语句的对象
String sql = "select * from products where id >20 or 1=1";
Statement st = conn.createStatement();
ResultSet set = st.executeQuery(sql);
//遍历结果集
while (set.next()) {
//获取字段值 为name的数据
String name = set.getString("name");
System.out.println(name);
}
// 释放资源
set.close();
st.close();
conn.close();
}
}

此处的sql语句中的“1=1”直接使得where条件无效,导致可以查出products表中的所有消息,解决方法

使用PrepareStatement对象

public class JdbcDemo3 {
public static void main(String[] args) throws Exception {
DriverManager.registerDriver(new Driver());
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_demo1", "root", "feng");
// 获取执行sql语句的对象
String sql = "select * from products where id >? ";
PreparedStatement ps = conn.prepareStatement(sql);
// 对sql语句进行预编译
// ? 占位符 ,1表示第一个?
ps.setString(1, "20 or 1=1");
ResultSet set = ps.executeQuery();
//遍历结果集
while (set.next()) {
//获取字段值 为name的数据
String name = set.getString("name");
System.out.println(name);
}
// 释放资源
set.close();
ps.close();
conn.close();
}
}

运行无结果,即阻止了sql注入

五.  数据库连接池

1. 什么是数据库连接池

  数据库连接池是程序员启动时建立足够多的数据库连接,并将这些连接组成一个连接池,由程序动态的对池中的连接进行申请,使用,释放。

  优点:节省创建连接与释放连接的性能消耗(即创建对象与回收对象的性能消耗);连接池中连接起到复用的作用 ,提高程序性能

2. 数据库连接池的运行机制

(1)程序初始化时,创建连接池

(2)使用时向连接池申请可用连接

(3)使用完毕,将连接返还给连接池

(4)程序退出时,断开所有连接,并释放资源

3. C3p0连接池(自动读取配置文件(底层封装了读取,加载配置文件的代码),加载四大参数,当连接断开时,其会自动尝试连接)   dbcp连接池(半自动)  druid(德鲁伊,阿里巴巴的开源连接池)

 4. C3p0的使用

4.1 添加两个jar包,并添加路径

4.2 数据库的原配置文件

(1)db.properties  前面的关键字可以不按规定写,但是需要自己加载,如下面这样写就需要自己加载配置文件

(2)以下两种配置方式,c3p0连接池会自动读取配置文件,数据源

第一种方式:c3p0.properties

  注意: c3p0.properties 文件名不能改,必须放在src下,配置文件中的key名称固定

案例1

product类(javabean)

public class Product {
int id ;
String name ;
double price ;
String category ;
int pnum ;
String description ;
// 省略。。。。。
}
public class C3p0Demo1 {
public static void main(String[] args) throws Exception {
// 操作数据库,连接池对象[四大参数]
ComboPooledDataSource dateSource = new ComboPooledDataSource();
//连接 ,获取Statement对象 resultset对象
QueryRunner runner = new QueryRunner(dateSource);
// 执行sql语句
String sql = "select * from products";
List<Product> list = runner.query(sql,new BeanListHandler<>(Product.class)); // 此处若是确定返回的是一条数据,则可以使用new BeanHandler<>(product.class)为参数
for (Product product : list) {
System.out.println(product);
}
}
}

运行结果

案例二

public class C3p0Demo2 {
public static void main(String[] args) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
QueryRunner runner = new QueryRunner(dataSource);
// 查
String sql = "select * from products where id=?";
// runner执行sql语句时会对其进行预编译
Product query = runner.query(sql, new BeanHandler<>(Product.class),"100 or 1=1");
Product query1 = runner.query(sql, new BeanHandler<>(Product.class),1); //返回单条数据
System.out.println(query);//null
System.out.println(query1);//Product [id=1, name=奋斗, price=100.0, category=励志, pnum=100, description=一次心灵的鸡汤]
}
}

注意,若以上的product.class定义的字段(属性),与数据库中获取的字段不一致,则数据存不进product这个javabean中的相应字段中去,如数据库中的products表格中有name字段,若将product这个javabean的name字段改为userName,则获取到的name属性将不能封装进product这个javabean中,userName值将全为空

第二种形式(不大懂)

  可以配置多个数据库连接并且可以指定名字去加载

零基础学习java------25--------jdbc的更多相关文章

  1. 音乐出身的妹纸,零基础学习JAVA靠谱么

    问:表示音乐出身的妹纸一枚  某一天突然觉得身边认识的是一群程序员   突然想 要不要也去试试... 众好友都觉得我该去做个老师,可是我怕我会误人子弟,祸害祖国下一代..... 要不要 要不要 学Ja ...

  2. 总结了零基础学习Java编程语言的几个基础知识要点

    很多Java编程初学者在刚接触Java语言程序的时候,不知道该学习掌握哪些必要的基础知识.本文总结了零基础学习Java编程语言的几个基础知识要点. 1先了解什么是Java的四个方面   初学者先弄清这 ...

  3. 零基础学习hadoop到上手工作线路指导(中级篇)

    此篇是在零基础学习hadoop到上手工作线路指导(初级篇)的基础,一个继续总结. 五一假期:在写点内容,也算是总结.上面我们会了基本的编程,我们需要对hadoop有一个更深的理解: hadoop分为h ...

  4. 零基础学习hadoop到上手工作线路指导初级篇:hive及mapreduce

      此篇是在零基础学习hadoop到上手工作线路指导(初级篇)的基础,一个继续总结.五一假期:在写点内容,也算是总结.上面我们会了基本的编程,我们需要对hadoop有一个更深的理解:hadoop分为h ...

  5. 【零基础学习iOS开发】【转载】

    原文地址:http://www.cnblogs.com/mjios/archive/2013/04/24/3039357.html 本文目录 一.什么是iOS 二.主流手机操作系统 三.什么是iOS开 ...

  6. 李洪强iOS开发之【零基础学习iOS开发】【01-前言】01-开篇

    从今天开始,我就开始更新[零基础学习iOS开发]这个专题.不管你是否涉足过IT领域,也不管你是理科生还是文科生,只要你对iOS开发感兴趣,都可以来阅读此专题.我尽量以通俗易懂的语言,让每个人都能够看懂 ...

  7. 零基础学习hadoop到上手工作线路指导

    零基础学习hadoop,没有想象的那么困难,也没有想象的那么容易.在刚接触云计算,曾经想过培训,但是培训机构的选择就让我很纠结.所以索性就自己学习了.整个过程整理一下,给大家参考,欢迎讨论,共同学习. ...

  8. 【零基础学习iOS开发】【01-前言】01-开篇

    本文目录 一.什么是iOS 二.主流手机操作系统 三.什么是iOS开发 四.学习iOS开发的目的 五.学习iOS开发的前提 从今天开始,我就开始更新[零基础学习iOS开发]这个专题.不管你是否涉足过I ...

  9. 零基础学习iOS开发

    零基础学习iOS开发不管你是否涉足过IT领域,只要你对iOS开发感兴趣,都可以阅读此专题. [零基础学习iOS开发][02-C语言]11-函数的声明和定义 摘要: 在上一讲中,简单介绍了函数的定义和使 ...

  10. salesforce 零基础学习(六十八)http callout test class写法

    此篇可以参考: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restfu ...

随机推荐

  1. hdu 4788 Hard Disk Drive (水题)

    题意: Input The first line contains an integer T, which indicates the number of test cases. For each t ...

  2. linux 内核源代码情景分析——linux 内核源码中的汇编语言代码

    1. 用汇编语言编写部分核心代码的原因: ① 操作系统内核中的底层程序直接与硬件打交道,需要用到一些专用的指令,而这些指令在C语言中并无对应的语言成分: ② CPU中的一些特殊指令也没有对应的C语言成 ...

  3. 安装配置多个版本JDK

    前言:JDK有多个版本,有时为了开发需要切换不同的版本,在一部电脑上安装多个JDK,只需要按以下配置,每次即可轻松使用.以下环境为Windows10 安装JDK 安装JDK8 配置环境变量 需要配置J ...

  4. Piakchu之RCE漏洞

    一.Ping(远程系统命令执行) 首先正常输入一个ip,查看页面的返回值.发现有乱码,但是能看出执行了ping命令. 查看源代码,可以看到只是对操作系统进行了判断,而对输入内容是否为ip地址并没有判断 ...

  5. seq2seq之双向解码

    目录 背景介绍 双向解码 基本思路 数学描述 模型实现 训练方案 双向束搜索 代码参考 思考分析 文章小结 在文章<玩转Keras之seq2seq自动生成标题>中我们已经基本探讨过seq2 ...

  6. WSL2学习和使用汇集

    接触WSL2过程中整理沉淀的一些知识点,大纲如下,内容比较多,详细内容参考https://www.yuque.com/wushifengcn/kb/mbg1b5 欢迎感兴趣者补充和提出问题,共同学习. ...

  7. sprint boot 手动快速创建web应用(2)

    1.打开Eclipse新建maven项目 2.导入maven依赖 <parent> <groupId>org.springframework.boot</groupId& ...

  8. C++ pragma once 与 ifndef 用法区别

    #pragma once 与 #ifndef 的作用 (1)在C/C++中,在使用预编译指令#include的时候,为了防止重复引用造成二义性. (2)在能够支持这两种方式的编译器上,二者并没有太大的 ...

  9. 设计模式--策略模式Strategy

    策略模式 算法经常需要被改变==使用S 节省资源(很多if else if-.不会被执行,却会被装载到代码段) 动机 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到 ...

  10. Django 小实例S1 简易学生选课管理系统 5 实现注册功能

    Django 小实例S1 简易学生选课管理系统 第5节--实现注册功能 点击查看教程总目录 作者自我介绍:b站小UP主,时常直播编程+红警三,python1对1辅导老师. 本文涉及到的新的额外知识点: ...