零基础学习java------25--------jdbc
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的更多相关文章
- 音乐出身的妹纸,零基础学习JAVA靠谱么
问:表示音乐出身的妹纸一枚 某一天突然觉得身边认识的是一群程序员 突然想 要不要也去试试... 众好友都觉得我该去做个老师,可是我怕我会误人子弟,祸害祖国下一代..... 要不要 要不要 学Ja ...
- 总结了零基础学习Java编程语言的几个基础知识要点
很多Java编程初学者在刚接触Java语言程序的时候,不知道该学习掌握哪些必要的基础知识.本文总结了零基础学习Java编程语言的几个基础知识要点. 1先了解什么是Java的四个方面 初学者先弄清这 ...
- 零基础学习hadoop到上手工作线路指导(中级篇)
此篇是在零基础学习hadoop到上手工作线路指导(初级篇)的基础,一个继续总结. 五一假期:在写点内容,也算是总结.上面我们会了基本的编程,我们需要对hadoop有一个更深的理解: hadoop分为h ...
- 零基础学习hadoop到上手工作线路指导初级篇:hive及mapreduce
此篇是在零基础学习hadoop到上手工作线路指导(初级篇)的基础,一个继续总结.五一假期:在写点内容,也算是总结.上面我们会了基本的编程,我们需要对hadoop有一个更深的理解:hadoop分为h ...
- 【零基础学习iOS开发】【转载】
原文地址:http://www.cnblogs.com/mjios/archive/2013/04/24/3039357.html 本文目录 一.什么是iOS 二.主流手机操作系统 三.什么是iOS开 ...
- 李洪强iOS开发之【零基础学习iOS开发】【01-前言】01-开篇
从今天开始,我就开始更新[零基础学习iOS开发]这个专题.不管你是否涉足过IT领域,也不管你是理科生还是文科生,只要你对iOS开发感兴趣,都可以来阅读此专题.我尽量以通俗易懂的语言,让每个人都能够看懂 ...
- 零基础学习hadoop到上手工作线路指导
零基础学习hadoop,没有想象的那么困难,也没有想象的那么容易.在刚接触云计算,曾经想过培训,但是培训机构的选择就让我很纠结.所以索性就自己学习了.整个过程整理一下,给大家参考,欢迎讨论,共同学习. ...
- 【零基础学习iOS开发】【01-前言】01-开篇
本文目录 一.什么是iOS 二.主流手机操作系统 三.什么是iOS开发 四.学习iOS开发的目的 五.学习iOS开发的前提 从今天开始,我就开始更新[零基础学习iOS开发]这个专题.不管你是否涉足过I ...
- 零基础学习iOS开发
零基础学习iOS开发不管你是否涉足过IT领域,只要你对iOS开发感兴趣,都可以阅读此专题. [零基础学习iOS开发][02-C语言]11-函数的声明和定义 摘要: 在上一讲中,简单介绍了函数的定义和使 ...
- salesforce 零基础学习(六十八)http callout test class写法
此篇可以参考: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restfu ...
随机推荐
- Centos 7 局域网 yum 源搭建
一.需求及实现方式介绍: 需求:现在各个软件版本更新迭代很快,在我们部署一套集群(比如:openstack)后,如果过一段时间想扩展集群时发现软件版本早已迭代更新,安装后导致和现有环境或多或少不兼容, ...
- Linux 安装nacos
1.已有mysql环境 2.解压文件 #tar -zxvf package/nacos-server-2.0.1.tar.gz 3.创建数据库nacos_config(confnacos-mysql. ...
- 【java+selenium3】特殊元素iframe的定位及详解(三)
一.iframe 内联框架 1.自己写个网页,仅供理解iframe演示使用,如下 <!DOCTYPE html> <html> <head> <meta ch ...
- 升级JDK8的坎坷之路
为更好的适应JAVA技术的发展,使用更先进及前沿的技术.所以推出将我们现在使用的JDK1.6(1.7)及tomcat6(7)升级至JDK1.8及tomcat8,使我们的系统获得更好的性能,更好适应未来 ...
- c++学习笔记5(函数的缺省参数)
例: void func(int x1,int x2=2,int x3=3){} func (10)//等效于func (10,2,3) func (10,8)//等效于func (10,8,3) f ...
- Linux基础二:文件系统
二.文件系统: Linux系统一切皆文件!整个文件系统是一棵颠倒过来的树形结构,根目录/在顶部,且从根目录到下面的任一文件有且仅有一条路径. 1.重要目录: /usr -> 存放普通用户命令(/ ...
- 菜鸡的Java笔记 日期操作类
日期操作类 Date 类与 long 数据类型的转换 SimpleDateFormat 类的使用 Calendar 类的使用 如 ...
- 使用VSCode编写,发布cnblogs
WriteCnBlog插件作者写的教程: https://www.cnblogs.com/caipeiyu/p/11774968.html
- [bzoj1982]Moving Pebbles
首先发现当n堆石子可以两两配对时,后手必胜,因为后手可以模仿先手那么当n堆石子不能两两配对时,先手必胜,因为先手可以做到让其两两配对,然后即先手必胜 这个东西用map维护即可 1 #include&l ...
- SpringCloud微服务实战——搭建企业级开发框架(二十一):基于RBAC模型的系统权限设计
RBAC(基于角色的权限控制)模型的核心是在用户和权限之间引入了角色的概念.取消了用户和权限的直接关联,改为通过用户关联角色.角色关联权限的方法来间接地赋予用户权限,从而达到用户和权限解耦的目的. R ...