Java_乔晓松_JDBC总结

时间:2021-08-15 15:01:09
JDBC开发

JDBC的开发过程:
1. 首先建立数据库,插入表的记录,新建一个Java项目,在项目下建立lib文件,并把mysql驱动导入到Java项目中lib文件中。
2. 编写代码,并把mysql驱动加载到Java项目中,构建路径
推荐方法:class.froName(“com.mysql.jdbc.Driver”);
3. 建立连接
String url=”jdbc:mysql://localhost:3306/xsgl”:
String user=”root”:
Sting pass=”root”:
Connection con=DriverManager.getConnection(url,user,pass);
4. 创建Statement对象
Statement st=con.createStatement();
5. 执行
ResultSet rs=st.excuteQuery(sql);
6. 遍历结果集
while(rs.next()){
}
7. 关闭资源
rs.close();
st.close();
con.close()
注意:关闭资源之前,必须判断rs,st,con是否为空,如果为空就不需要关闭,否则关闭资源。//if(rs!=null || st!=null || con!=null){
//关闭资源
rs.close();
st.close();
con.close()
}//if尽可能的写在finally中,确保资源的释放
Connection对象是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
为了确保资源的释放,尽量把资源释放代码放到finally语句块中。

Connection对象常用的方法有:
• createStatement():创建向数据库发送sql的statement对象。
• prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
• prepareCall(sql):创建执行存储过程的callableStatement对象。 
• setAutoCommit(boolean autoCommit):设置事务是否自动提交。 
• commit() :在链接上提交事务。
• rollback() :在此链接上回滚事务。
Statement对象常用方法:
• execute(String sql):用于向数据库发送任意sql语句
• executeQuery(String sql) :只能向数据发送查询语句。
• executeUpdate(String sql):只能向数据库发送insert、update或delete语句
• addBatch(String sql) :把多条sql语句放到一个批处理中。
• executeBatch():向数据库发送一批sql语句执行。 
ResultSet对象常用的结果集进行滚动的方法:
• next():移动到下一行
• Previous():移动到前一行
• absolute(int row):移动到指定行
• beforeFirst():移动resultSet的最前面。
• afterLast() :移动到resultSet的最后面。
提取代码到配置文件中,文件名.properties
在实际开发中为了避免数据库改动,需要把一些必要的代码写到一个单独的配置文件中,这就简化了对大量代码的改动:如
在驱动连接数据库时,String url=”jdbc:mysql://localhost:3306/xsgl”:
String user=”root”:
Sting pass=”root”:
像这些代码都可以提取到配置文件中,在配置文件中就可以写为user=root
在写代码需要使用配置文件的内容时,
InputStream in=DBManager.class.getClassLoader().getResourceAsStream("db.properties");
Properties pro=new Properties();
try {
pro.load(in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
driver=pro.getProperty("driver");
url=pro.getProperty("url");
user=pro.getProperty("user");
password=pro.getProperty("password");
}catch(Exception e){
}
通过输入流就可以读取获取到配置文件的内容,附加给创建的成员变量,并加以使用
当系统运行时,需要读取一下配置文件,以获取配置文件的内容

多线程
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。

多线程的优势:
1.提高界面程序的响应速度.
2.充分利用系统资源.

在线程编程中,继承Thread类或实现Runnable接口的类才具备多线程的能力。


线程的生命周期与主要的线程状态:
1. 新建状态(New).
2. 就绪状态(Runnable).
3. 运行状态(Run).
4. 阻塞状态(Block).
5. 终止状态(Dead).

线程的优先级用1~10表示,1表四优先级最高,默认值是5,每个优先级值对应Thread类中的一个公共静态常量。
如果有多个线程在等待,并不是优先级越高就肯定越早执行,只是获得的机会更多一些,因此通常情况下,不要依靠线程优先级来控制线程的状态。

线程调度
1. 线程睡眠sleep()
i. 调用sleep()方法使线程处于睡眠,线程由运行状态进入不可运行的状态,睡眠时间过后线程再次进入可运行状态。
2. 暂停线程yield()
i. 调用yield()方法可暂停当前线程执行,将CPU资源让出来,允许其他线程执行。
ii. Yield()的优点是保证有工作时不会让CPU闲置,主要用于编写多个合作线程,也适合于强制线程间的合作。
3. 连接线程join()
i. 调用join()方法可使当前线程暂停执行,等待调用该方法的线程的结束后再继续执行本线程。
4. 中断线程interrupt()
i. Interrupt()方法可为线程设置一个中断标记,以便于run()方法运行时使用isInterrupted()方法能够检测到。此时,线程在sleep()之类的方法中被阻塞时,由sleep()方法抛出一个InterruptException()异常,然后捕捉这个异常以处理超时。
ii. Interrupt()只是一个中断标记,并没有中断线程运行。

将线程标记为守护线程(后台线程):setDaemon(true); 注意该方法要在线程开启前使用。和前台线程一样开启,并抢资源运行,所不同的是,当前台线程都结束后,后台线程会自动结束。无论后台线程处于什么状态都会自动结束。

线程的同步
在多线程编程中,这种会被多个线程同时访问的资源叫做临界资源。

Synchronized关键字是一个修饰符,可以修饰方法或代码块。其作用是:对于同一个对象(不是一个类的不同对象),当多个线程都同时调用该方法或代码块时,必须一次执行,也就是说,如果两个或两个以上的线程同时执行该段代码,如果一个线程已经开始执行该段代码,则另外一个线程必须等待这个线程执行完这段代码才能开始执行。
多线程的同步提高了系统的安全问题

线程同步的两种表现形式:
1.同步代码块。

synchronzied(对象锁){
需要被同步的代码。(哪些需要同步哪些不需要一定要分清)
}

2.同步函数。

就是在函数上加了synchronzied关键字进行修饰。、
同步代码块可以使用任意对象作为锁。
同步函数使用的锁只有一个,就是this。
注意:static同步函数使用的锁是该函数所属类的对象。类名.class
售票系统中的线程同步方法:
package com.hbsi;
//模拟临界资源的类
class Tickets{
public int tickets;
public Tickets(){
tickets=10;
}
public synchronized void action(String name){
System.out.println(name+"抢到了第"+tickets+"号票");
tickets--;
}
}
//访问数据的线程
class TicketsThread extends Thread{
Tickets t;
String name;
public TicketsThread(Tickets t,String name){
this.t=t;
this.name=name;
start();
}
@Override
public void run() {
try {
for(int i=0;i<5;i++){
t.action(name);
Thread.sleep(20);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
//测试多线程访问时的问题 
public class TestMulThread2{
public static void main(String[] args) {
// TODO Auto-generated method stub
Tickets t=new Tickets();
TicketsThread d1=new TicketsThread(t,"小王");
TicketsThread d2=new TicketsThread(t,"小张");
}
}
运行结果:
小王抢到了第10号票
小张抢到了第9号票
小王抢到了第8号票
小张抢到了第7号票
小王抢到了第6号票
小张抢到了第5号票
小王抢到了第4号票
小张抢到了第3号票
小王抢到了第2号票
小张抢到了第1号票

售票系统中的线程同步代码块:
package com.hbsi;


public class ticket1 implements Runnable{
private int ticket=100;
@Override
public void run() {
// TODO Auto-generated method stub
//Object obj=new Object();
while(true){
synchronized(this){
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"..."+ticket--);
}else{
break;
}
}
}

}
public static void main(String[] args) {
// TODO Auto-generated method stub
ticket1 t=new ticket1();
Thread td1=new Thread(t);
Thread td2=new Thread(t);
td1.start();
td2.start();
}
}

单例类懒汉式的线程同步:
Public class Single{
                  privatestatic Single s = null;
                  privateSingle(){}
                  publicstatic Single getInstance(){
                            if(s==null){
                                    synchronized(Single.class)
                                    {
                                              if(s==null)
                                                  s= new Single();
                                    }
                            }
                            return s;
                  }
        }
线程同步以后,懒汉式的安全性就进一步的提高。

线程的死锁:
产生死锁的原因主要是 
因为系统资源不足。 
进程运行推进的顺序不合适。 
资源分配不当等。 
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。 

产生死锁的四个必要条件 
互斥条件:一个资源每次只能被一个进程使用。 
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

线程间的通信
  两个或者两个以上的线程在处理同一个资源时,处理动作是不一样的。
  这时就需要将动作代码分别存放在不同的run方法,而run方法要封装单独的类中。
        示例:生产者,消费者。
-------------------------------------------------
        在同步中的使用的方法。
        wait,notify,notifyAll。这三个方式都定义Object类中。
        wait:让当前线程处于等待(冻结状态)状态。将该线程存入到了线程池中。
        notify:唤醒被等待的线程。通常唤醒的是线程池中的第一个。
        notifyAll:唤醒线程池中所有被等待线程。
        这些方法要用在同步中。并要明确三个方法所属的同步锁。
        synchronized(obj)
        {
                  obj.wait();
                  obj.notify()
        }
        在同步中无论被等待多少个线程,同时唤醒后,
        注意:只可能有一个线程会持有锁,并执行。其他线程即使子啊同步块中,也执行不了。
--------------------------------------------------- 
        wait(): 释放cpu的执行权。释放锁。
        sleep():释放cpu的执行权。不释放锁。


多线程在开发中的应用
1.下载
2.聊天

内部类中的多线程
public class ThreadDemo6 {

    /**
    * @param args
    */
    public static void main(String[] args) {
        
        new Thread(){
            public void run(){
                for(int i=0;i<100;i++){
                System.out.println("a="+i);
                }
            }
        }.start();
        
        for(int i=0;i<100;i++){
            System.out.println("b="+i);
        }
        
        Runnable r=new Runnable(){

            @Override
            public void run() {
                for(int i=0;i<100;i++){
                    System.out.println("c="+i);
                }
            }
            
        };
        new Thread(r).start();
    }
}

JDBC开发----数据库中大文本以及多媒体文件的插入读写
111307156 CSDNJava班 乔晓松
★★★
在开发中,需要把一些大文本或二进制的数据保存到数据库中,大数据称为lob,lob分2中:clob和blob;
★ clob:用于存储大文本,即text。
★ blob:用于存储二进制数据,即:图片,歌曲,二进制文件等数据。
★对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是Text,Text和blob分别又分为:
TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB 
在建立表结构时大文本的数据类型就定义为:text,二进制数据就定义为:blob。

★ Text类型的调用此方法设置:
PreparedStatement.setCharacterStream(index, reader, length);
//注意length长度须设置,并且设置为int型
Text类型的获取方法:
reader = resultSet. getCharacterStream(i);

★ blob类型的调用此方法设置:
PreparedStatement. setBinaryStream(i, inputStream, length);
//注意length长度须设置,并且设置为int型
blob类型的获取方法:
InputStream in  = resultSet.getBinaryStream(i);

★ JDBC进行批处理
多处理多条sql语句时,建议采用JDBC的批处理机制,以提高执行效率。
实现批处理有2中方式:
第一种方法:Statement.addBatch(sql)  list
执行批处理语句
executeBatch()方法:执行批处理命令
clearBatch()方法:清除批处理命令

举例:
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
String sql1 = "insert into user(name,password,email,birthday) 
values('kkk','123','abc@sina.com','1978-08-08')";
String sql2 = "update user set password='123456' where id=3";
st = conn.createStatement();
st.addBatch(sql1);  //把SQL语句加入到批命令中
st.addBatch(sql2);  //把SQL语句加入到批命令中
st.executeBatch();
} finally{
JdbcUtil.free(conn, st, rs);
}

采用Statement.addBatch(sql)方式实现批处理:
★ 优点:可以向数据库发送多条不同的SQL语句。
★ 缺点:
SQL语句没有预编译。
当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。例如:
Insert into user(name,password) values(‘aa’,’111’);
Insert into user(name,password) values(‘bb’,’222’);

第二种方法:
PreparedStatement.addBatch()

举例:
package Demo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import Util.DBManager;
/**
* 利用批处理向数据库中插入大文本和图片
* @author 乔晓松
*
*/
public class Person {

/**
* @param args
*/
public void insert(){
Connection con=DBManager.getConnection();
PreparedStatement st=null;

String sql="insert into person(id,name,headimg,intro) values(?,?,?,?)";
try {
//执行sql语句
st=con.prepareStatement(sql);
//批处理语句
String[] name={"aa","bb","cc"};
String[] img={"src/Demo/1.gif","src/Demo/2.gif","src/Demo/3.gif"};
String[] text={"src/Dao/Demo1.java","src/Demo/Test.java","src/Util/DBManager.java"};
//利用for循环赋值
for(int i=1;i<=3;i++){
st.setInt(1, i);
st.setString(2, name[i-1]);
File f1=new File(img[i-1]);
FileInputStream fis=new FileInputStream(f1);
st.setBinaryStream(3, fis, f1.length());

File f2=new File(text[i-1]);
BufferedReader br=new BufferedReader(new FileReader(f2));
st.setCharacterStream(4, br, f2.length());
//添加到批处理
st.addBatch();
}
//执行批处理
int[] x=st.executeBatch();
if(x.length==3){
System.out.print("插入成功!!!");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//关闭资源
DBManager.dbClose1(st, con);
}
}
// 主方法
public static void main(String[] args) {
//创建对象
Person p=new Person();
//执行insert方法,出入数据 
p.insert();
}

}

采用PreparedStatement.addBatch()实现批处理
★ 优点:发送的是预编译后的SQL语句,执行效率高。
★ 缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。

JDBC—存储过程以及事务处理
存储过程的参考写法:
delimiter $$
create procedure myproc(in uid char(8),out uname char(20))
begin
select name into uname from test where id=uid;
end $$
delimiter ;
调用存储过程:
call myproc(id,@name);
查看参数2:select @name;

JDBC调用存储过程
CallableStatement cs = conn.prepareCall("{call myproc(?, ?)}");
设置参数
cs.setString(1, id); //设置第一个参数
cs.registerOutParameter(2, Types.VARCHAR); //设置第二个参数的参数类型
cs.execute(); //执行语句
System.out.println(cStmt.getString(2)); //输出第二个参数

事务的概念
事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。
设置事务回滚点
• Savepoint sp = conn.setSavepoint();
• Conn.rollback(sp);
• Conn.commit();  //回滚后必须要提交

创建JDBC事务的步骤如下:
1. 将事务的提交方式设置为非自动提交
i. con.setAutoCommit(false);

2. 将需要添加事务的代码放入try,catch块中
3. 在try块内添加事务的提交操作,表示操作无异常,提交事务。 
    conn.commit();
4.在catch块内添加回滚事务,表示操作出现异常,撤销事务:
    conn.rollback();
5.设置事务提交方式为自动提交:
    conn.setAutoCommit(true);

事务的特性:
原子性
一致性
隔离性
持久性
事务的隔离性
脏读:指一个事务读取了另外一个事务未提交的数据
不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同
虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致

事务隔离性的设置语句
数据库共定义了四种隔离级别:
• Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)(序列化)
• Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)
• Read committed:可避免脏读情况发生(读已提交)。
• Read uncommitted:最低级别,以上情况均无法保证。(读未提交)
set  transaction isolation level 设置事务隔离级别
select @@tx_isolation 查询当前事务隔离级别