一、JDBC的原理
1、概念
Java DataBase Connectivity:java数据库连接技术,简称JDBC.
它是使用java语言编写的一套API,为多种关系型数据库提供了统一接口。 同时,也是一个“低级”接口,在其之上可以使用“高级”接口,更方便的连接数据库。
2、目的
为了给程序员提供同一种方式连接不同的数据库
(提供一个与任何数据库连接的java界面)
3、原理
JDBC负责提供接口
数据库厂商使用自己数据库的特点来实现接口。
程序员调用接口,实际上底层调用数据库厂商实现的部分
实际工作过程:
(1)先将驱动包导入项目下(引用和复制到项目下两种方式)
(2)加载驱动,建立连接
(3)使用驱动管理类型获取Sql语句执行对象
(4)编写SQL语句,执行SQL语句
(5)处理结果集
(6)关闭连接
4、接口与类型
(1)驱动管理类型:DriverManager
static Connection getConnection(url,user,password)
作用: 通过地址,数据库用户名,用户密码-----获取连接对象
(2)连接接口:Connection
Statement createStatement()
作用:获取一个SQL语句编译对象
Statement st=conn.createStatement();
PreparedStatement prepareStatement(String sql)
作用:获取一个SQL语句预编译对象
PreparedStatement ps=conn.prepareStatement(insert);
(3)SQL语句对象接口:
Statement: 用于编译静态SQL语句(编译多次)
Statement st=conn.createStatement();
boolean execute(String sql):用来执行DDL语言
public static boolean login(String user,String pwd){
ResultSet executeQuery(String sql):用于执行DQL语言
String sql="select * from t_t01 where tname='zs'";
ResultSet rs=st.executeQuery(sql);
int executeUpdate(String sql):用于执行DML语言
String sql="update t_t02 set tsalary='3500'where tname='zs'";
int rs=st.executeUpdate(sql);
ps.executeUpdate();
void addBatch(String sql):添加批处理
ps.addBatch();
if(i%22==0){
//执行一次批处理
ps.executeBatch();
}
int[] executeBatch(): 执行批处理
PreparedStatement:用于编译静态SQL语句,是Statement的一个子类型,执行效率比Statement要高(只编译一次)
//添加手动批处理
conn.setAutoCommit(false);
习题:JDBC_day02
package com.hyxy.jdbc.day02;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TestBatch01 {
@Test
public void TestStatement02(){
try {
Statement stat=conn.createStatement();
long start=System.currentTimeMillis();
conn.setAutoCommit(false);
for(int i=0;i<100;i++){
String account_idcard=10000000000000000L+(long)(Math.random()*100000000000000L)+"";
double money=Math.random()*1000;
String account_phone=10000000000L+(long)(Math.random()*90000000000L)+"";
String sql="insert into account_info values(null,'"+account_idcard+"',"+money+",'"+account_phone+"')";
//添加批处理
stat.addBatch(sql);
if(i%22==0){
stat.executeBatch();
conn.commit();
stat.executeBatch();
}
}
//将批处理剩余的不满的22条记录的一次性执行出来
stat.executeBatch();
conn.commit();
long end=System.currentTimeMillis();
System.out.println("statement批处理时间:"+(end-start));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testpreparedStatement02(){
try {
String sql="insert into account_info values(null,?,?,?)";
PreparedStatement ps=conn.prepareStatement(sql);
long start=System.currentTimeMillis();
//添加手动批处理
conn.setAutoCommit(false);
for(int i=0;i<100;i++){
String account_idcard=1000000000000000L+(long)(Math.random()*900000000000000L)+"";
double money=Math.random()*1000;
String account_phone=10000000000L+(long)(Math.random()*90000000000L)+"";
ps.setString(1, account_idcard);
ps.setDouble(2, money);
ps.setString(3, account_phone);
//添加批处理
ps.addBatch();
if(i%22==0){
//执行一次处理
ps.executeBatch();
conn.commit();
ps.executeBatch();
}
}
ps.executeBatch();
conn.commit();
long end=System.currentTimeMillis();
System.out.println("preparement批处理时间:"+(end-start));
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
//插入100条记录不用批处理
public void testStatement01(){
try {
Statement stat=conn.createStatement();
long start=System.currentTimeMillis();
for(int i=0;i<100;i++){
String account_idcard=10000000000L+(long)(Math.random()*90000000000L)+"";
double money=Math.random()*1000;
String account_phone=10000000000L+(long)(Math.random()*90000000000L)+"";
String sql="insert into account_info values(null,'"+account_idcard+"',"+money+",'"+account_phone+"')";
stat.executeUpdate(sql);
}
long end=System.currentTimeMillis();
System.out.println("时间毫秒数:"+(end-start));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testPreparedStatement01(){
try {
String sql="insert into account_info values(null,?,?,?)";
PreparedStatement ps=conn.prepareStatement(sql);
long start=System.currentTimeMillis();
for(int i=0;i<100;i++){
String account_idcard=1000000000000000L+(long)(Math.random()*9000000000000000L)+"";
double money=Math.random()*1000;
String account_phone=10000000000L+(long)(Math.random()*90000000000L)+"";
ps.setString(1, account_idcard);
ps.setDouble(2, money);
ps.setString(3, account_phone);
ps.executeUpdate();
}
long end=System.currentTimeMillis();
System.out.println("时间毫秒数:"+(end-start));
} catch (Exception e) {
e.printStackTrace();
}
}
private Connection conn;
@Before
public void connectionDatabase(){
try {
conn=DBUtil.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
@After
public void closeDatabase(){
DBUtil.closeConnection(conn);
}
}
(4)结果集接口:ResultSet
在进行DQL操作时, 将查询的所有记录信息封装到ResultSet对象中 .
boolean next(): 询问结果集对象中是否有下一行记录。
如果返回true,光标会自动移到下一行。
while(rs.next()){
还提供了一些数据库类型转java数据类型的方法:
int getInt(int columnIndex)/getInt(String columnName)
double getDouble(int columnIndex)/getDouble(String columnName)
String getString(int columnIndex)/getString(String columnName)
Date getDate(int columnIndex)/getDate(String columnName)
.........
5、驱动
Oracle
jar包:ojdbc5.jar/ojdbc14.jar
Class.forName("oracle.jdbc.driver.OracleDriver");
Mysql
jar包: mysql-connector-java-5.1.7-bin.jar
Class.forName("com.mysql.jdbc.Driver");
二、JDBC编程基础
.......
三、工具类的封装DBUtil
从上述练习中发现,每次都需要加载驱动,建立链接。
其中的参数我们书写的次数比较多。
工具类的封装:
1、设置静态成员变量
2、使用静态块进行加载资源
3、将连接数据库操作单独封装一个工具方法
4、将关闭数据库操作单独封装一个工具方法
数据库的工具类:使用Properties读取配置文件的方式
方法1:
为了方便,为了节省资源,我们可以使用静态块来加载驱动,将参数值当成类的静态属性进行封装。 (加载一次就可以)
方法2:
使用Properties 读取配置文件里的信息(将可能需要改变的信息存储在配置文件中)
通常情况下都会选择方法2:
InputStream is=DBUtil02.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop=new Properties();
prop.load(is);
river=prop.getProperty("driver");
练习在DBUtil02
package com.hyxy.jdbc.day02;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/*数据库的工具类:使用properties读取配置文件的方式*/
public class DBUtil02 {
private static String driver;
private static String url;
private static String user;
private static String password;
static {
try {
/*先使用Properties读取配置文件里的信息*/
InputStream is=DBUtil02.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop=new Properties();
//将流里的信息加载到对象上
prop.load(is);
driver=prop.getProperty("driver");
url=prop.getProperty("url");
password=prop.getProperty("password");
user=prop.getProperty("user");
//加载驱动类型
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
/*连接数据库的工具方法
* @return 连接成功后的连接对象
* @throws SQLException*/
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, user, password);
}
/*关闭连接操作的工具方法
* @param conn要关闭的连接对象
* 返回值是要关闭的连接对象*/
public static void closeConnection(Connection conn){
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/*测试连接方法的功能是否有效
* @param args*/
public static void main(String[] args) {
try {
Connection conn=getConnection();
System.out.println(conn);
closeConnection(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
四:异常
jdbc发生的异常一般都是SQLException或者是其子类异常是检查性异常。
使用try-catch机制处理一下。
finally里通常用来进行关闭数据库操作。
五、SQL注入:(injection)
在使用Statement时,因为Statement会对静态SQL语句进行编译, 有可能会改变SQL语句的结构。因此有注入安全隐患。
创建一个表 login_info
字段: login_id int 主键约束 序列自增
login_user varchar(20) 唯一约束
login_pwd varchar(50) 非空约束
login_phone varchar(11)
插入两条记录:
1001,'zhangsan123','111111','13100001111'
1002,'lisi123456','888888','13700001111'
create table login_info(
login_id int primary key auto_increment,
login_user varchar(20) unique,
login_pwd varchar(50) not null,
login_phone varchar(11)
);
insert into login_info values(1001,'zhangsan123','111111','13100001111');
insert into login_info values(1002,'lisi123456','888888','13700001111');
select * from login_user='zhangsan123' and login_pwd='wang';
由上面两个条件的sql结构
变成了下面的三个条件的结构,就是SQL注入现象
select * from login_user='zhangsan123' and login_pwd='wang' or 'a' = 'a';
后来改用Statement的子类PreparedStatement来代替。
PreparedStatement叫预编译sql语句类型,对静态sql语句编译一次。编译后无法再改变sql语句结构
六、PreparedStatement的用法
可以对静态SQL语句使用问号"?"充当占位符。
如:
select * from login_info where login_user = ? and login_pwd= ?
使用此类中提供的类型转换方法进行给问号赋值
ps.setInt(int parameterIndex,int value)
ps.setDouble(int parameterIndex,double value)
ps.setString(int parameterIndex,String value)
ps.setDate(int parameterIndex,Date value)
.........
ps.setInt(1,9100);
ps.setString(2,"superman");
ps.setString(3,"saleman");
ps.setInt(4, 9001);
ps.setDate(5, Date.valueOf("1987-10-12"));
ps.setDouble(6,3000);
ps.setDouble(7,20);
今天的习题代码是eclipse--day01
package com.hyxy.jdbc_day01;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
/*预编译SQL语句类型的学习
* PreparedStatement:是Statement的子类*/
public class TestPreparedStatementDemo01 {
public static void main(String[] args) {
/*向员工表中插入一条记录
* 9100,'superman','salesman','9001',null,3000.0,20,10
* */
Connection conn=null;
try {
conn=DBUtil02.getConnection();
String insert="insert into emp values(?,?,?,?,?,?,?,?)";
PreparedStatement ps=conn.prepareStatement(insert);
ps.setInt(1,9100);
ps.setString(2,"superman");
ps.setString(3,"saleman");
ps.setInt(4, 9001);
ps.setDate(5, Date.valueOf("1987-10-12"));
ps.setDouble(6,3000);
ps.setDouble(7,20);
ps.setInt(8,10);
//执行sql语句
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
DBUtil02.closeConnection(conn);
}
}
}
新课
一、Junit: java方法的另外一种测试方式
(1)导入Junit4的jar
(2)在非静态方法上添加注解@Test
(3)选中非静态方法的名称右键运行或者在类的树形结构中选中方法运行。
@Before:此注解的位置也是非静态方法上。
运行时机:在运行@Test注解方法前会自动执行。
@After:在运行@Test注解方法后会执行
二、JDBC对事务的支持
事务要满足四个条件:ACID
原子性:一个事务,要么成功,要么回滚(撤回)
一致性:事务开始前的数据要和结束后的数据保持一致。
隔离性:一个事务正在进行,另外的事务要等待。
持久性:事务提交后,数据的改变是永久性的。
手动提交:
JDBC对DML语言的操作是默认提交的,当有多个DML操作时,我们应该取消自动提交,改为手动提交 。
Connection接口提供了一个方法
void setAutoCommit(boolean flag):
true:表示自动提交
false:表示取消自动提交
void commit(): 提交事务
void rollback():事务回滚
三、批处理
在进行插入数据操作时,有的时候,一条一条的操作,比较耗时。
我们可以进行批量的插入操作。
Statement:
addBatch(String sql)
executeBatch():
PreparedStatement:
addBatch():
executeBatch():
四、新课
1、oracle学习版安装
(1)添加SYS和System用户的口令
(2)我们要启动数据库主页,使用System进行登录
(3)创建一个普通用户scott,密码tiger
2、 连接方式
(1)命令提示符界面
选择自带的运行SQl命令行界面
输入 conn username/password
(2)使用客户端sqldeveloper
连接名:
用户名:scott
口令:
主机名:ip
端口号: tcp/ip协议,1521 /http协议的端口号默认是8080/80
SID:oracle特有的唯一标识符 xe/orcl
(3)使用JDBC连接oralce数据库
3、使用
数据类型: 数字类型
字符串类型
日期类型
数字类型:number
number(m):表示最长m位整数
number(m,n):表示整数位最长m-n,小数点后保留n位。
字符串类型:
char(m):固定长度字符串类型
varchar(m)
varchar2(m):oracle独有的一个可变字符串类型
Blob
日期类型:date: 年月日时分秒
timestamp:可以精确到纳秒
作业:
在TestOracle里使用Junit进行
完成增删改查操作
查: 通过tid查询某一条记录
查全部
testAdd()
testByTid()
testAll()
testUpdate()
testdeleteByTid()
package com.hyxy.jdbc.day03;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/* 在TestOracle里使用Junit进行
完成增删改查操作
查: 通过tid查询某一条记录
查全部
testAdd()
testByTid()
testAll()
testUpdate()
testdeleteByTid()
*/
public class TestOracle {
private Connection conn;
@Test
public void testBytid(){
try {
String sql="select * from t_01 where tid=?";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setInt(1,1002);
ResultSet rs=ps.executeQuery();
if(rs.next()){//查询一条用if
System.out.println(rs.getInt(1)+","+rs.getString(2)+","+rs.getString(3)+","+rs.getString(4)+","+rs.getDouble(5));
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testUpdate(){
try {
String sql="update t_01 set tname=? where tid=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "wanger");
ps.setInt(2,1001);
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testdeleteByTid(){
try {
String sql="delete from t_01 where tid=1003";
PreparedStatement ps = conn.prepareStatement(sql);
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testAll(){
try {
String sql="select * from t_01";
PreparedStatement ps=conn.prepareStatement(sql);
ResultSet rs=ps.executeQuery();
while(rs.next()){//查询多条用while
System.out.println(rs.getInt(1)+","+rs.getString(2)+","+rs.getString(3)+","+rs.getString(4)+","+rs.getDouble(5));
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testAdd(){
try {
//插入2条数据
String sql="insert into t_01 values(?,?,?,?,?)";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setInt(1, 1002);
ps.setString(2, "zhaoliu");
ps.setString(3,"1230000000000000");
ps.setString(4, "f");
ps.setDouble(5,5000.0);
ps.execute();
ps.setInt(1, 1003);
ps.setString(2, "zhaoqi");
ps.setString(3,"1240000000000000");
ps.setString(4, "m");
ps.setDouble(5,6000.0);
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
@Before
public void connectionDatabase(){
try {
conn=DBUtil.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
@After
public void colseConnection(){
DBUtil.closeConnection(conn);
}
}
新课
一.Mysql自增序列的获取
有的时候,两张表中的字段可能有关系,当向一张表插入数据时,另外一张表可能需要这张表的数据。
Student
Sid sname sage
1001 ‘zhangsan’ 23
Sc选课表
Scid sid scname
1 1001 ‘高数
分页查询---TestByPage.java
limit brgin , size.
limit size;
package com.hyxy.jdbc.day03;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TestByPage {
@Test
public void findByPage(){
try {
/*查询员工表的第二页数据,按照部门升序每页5条*/
String sql="select * from emp order by deptno limit ?,?";
PreparedStatement ps=conn.prepareStatement(sql);
/*page:n页数
* 第n页的数据
* size:每页最大数
* begin :size*(page-1)*/
ps.setInt(1,5*(2-1));
ps.setInt(2, 5);
ResultSet rs=ps.executeQuery();
while(rs.next()){//查询一条用if
System.out.println(rs.getInt("deptno")+","+rs.getInt("empno")+","+rs.getString("ename"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
private Connection conn;
@Before
public void connectionDatabase(){
try {
conn=DBUtil.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
@After
public void colseConnection(){
DBUtil.closeConnection(conn);
}
}
---------------------------------------------------------------------------------------------------------------------------------
新课2
思考:表,记录与类,对象关系
三.DAO(Data Access Object)思想:数据访问对象
在开发时,我们一定会使用数据库,编程语言的APP
存储
APP--------------------->数据库
<---------------------
获取
ORM:对象关系映射(Object relation mapping)
表的一条记录映射成类的一个对象
类的一个对象映射成一个表的记录
开发思路:
现在主流开发模式:MVC架构思想
Model模型层--业务层,持久层
View视图层--用户所能操作与看到的部分
Controller控制层--控制用户操作与模型层的逻辑走向部分
程序与数据库交互的层次称之为持久层
第一步: 通过ORM(对象关系映射)设计实体类
第二步:设计一个接口(持久层的各个功能),提供与数据库交互的各个功能
第三步:编写此接口的实现类,完善功能逻辑
第四步:提供Dao工厂类型
四.在开发项目时需要注意的:
包的设计:域名后缀.域名.项目名.模块名
com.hyxy.jdbc.util
com.hyxy.jdbc.entity
com.hyxy.jdbc.dao
com.hyxy.jdbc.dao.impl
com.hyxy.jdbc.test
习题: JDBC_day003和JDBC_day03
---------------------------------------------------------------------------------------------------------------------------------------------
Oracle的分页查询
1.按照排序规则
select * from emp order by dptno
2.在分配行号
select rownum rn,e.* from (select * from emp order by dptno) e;
3.分页查询,可以进行范围确定
select from
select rownum rn,e.* from (select * from emp order by dptno
) e where rn between 6 and 10;