Java EE筆記 (1) JDBC

JDBC(全稱爲:Java Data Base Connectivity)
共兩個包:
java.sql
javax.sql
工做流程:
應用程序—JDBC—MySQL驅動或Oracle驅動—MySQL數據庫或Oracle數據庫
MySQL驅動:mysql-connector-java-5.0.8-bin.jar
Oracle驅動:ojdbc14.jar

1、JDBC工做六大步驟:
1.加載驅動
DriverManager.registerDriver("com.mysql.jdbc.Driver") 
不推薦,這種形式會形成JVM虛似機內存中產生兩個同樣的Driver對象。
Class.forName("com.mysql.jdbc.Driver");
推薦這種方式,不會對具體的驅動類產生依賴。

2.創建鏈接
Connection conn = DriverManager.getConnection(url,user,pass); //user與pass爲登錄數據庫的用戶名與密碼
也可採用這種方式
Connection conn = DriverManager.getConnection(url?user=root&&password=root); //用戶名與密碼以參數帶在後面
協議:子協議://主機:端口/數據庫名
MySQL的url:jdbc:mysql://localhost:3306/數據庫名
Oracle的url:jdbc:oracle:thin:@localhost:1521:數據庫名
SQLServer的url:jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=數據庫名

3.建立執行SQL的語句
(1)採用Statement實現,有被SQL注入的危險,如:('or 1=1 or name=') ,且可能形成數據庫緩衝區溢出:
Statement st = conn.createStatement();

(2)採用PreparedStatement實現,此類安全,可對SQL進行預編譯:
String sql = "insert into 表名(id,name,birthday,mytext,myblob) values(?,?,?,?,?);
PreparedStatement st = conn.prepareStatement(sql);

conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);//該常量指示生成的鍵能夠獲取。
st.setInt(1,1);  //對應第1個?號
st.setString(2,"wudi");  //對應第2個?號
//對應第3個?號,日期存入數據庫時要轉成sql類型的Date,getTime()方法將日期轉成毫秒數字
st.setDate(3, new java.sql.Date(new java.util.Date().getTime()));  
//對應第4個?號,存大文本
URL url = 類.class.getClassLoader().getResource("a.txt");//獲得文件虛擬地址
File file = new File(url.getPath()); //獲得文件真實路徑並用file鏈接
st.setCharacterStream(4, new FileReader(file), (int) file.length());
//對應第5個?號,存二進制文件
URL url = 類.class.getClassLoader().getResource("a.jpg");//獲得文件虛擬地址
File file = new File(url.getPath()); //獲得文件真實路徑並用file鏈接
st.setBinaryStream(2, new FileInputStream(file), (int) file.length());  //MySql數據庫存的方式

4.執行語句
ResultSet rs = st.getGeneratedKeys();  //獲得插入行的主鍵,只對insert有用
ResultSet rs = st.excute(sql);       //任意操做
ResultSet rs = st.excuteQuery(sql);  //查詢時
ResultSet rs = st.excuteUpdate(sql); //增刪改時
//可實現批處理sql語句
Statement.addBatch(sql)  
優勢:能夠向數據庫發送多條不一樣的sql語句。
缺點:sql語句沒有預編譯
PreparedStatement.addBatch(sql)
優勢:發送的是預編譯後的SQL語句,執行效率高。
缺點:只能應用在sql語句相同,但參數不一樣的批處理中。

(1)小例子:採用Preparedstatement.addBatch(sql)方法實現sql語句批處理,
String sql = "insert into tbatch(id,name,password) values(?,?,?)";
st = conn.prepareStatement(sql);
for(int i=1;i<500003;i++){
  st.setInt(1, i);
  st.setString(2, "aa" + i);
  st.setString(3, "1111" + i);
  //程序執行到此,st對象裏面已是一條完整的sql,能夠加到 batch裏面去了
  st.addBatch();
  if(i%1000==0){//batch最大可容1000條sql語句
      st.executeBatch();  //執行來1次batch裏的全部sql,
      st.clearBatch();  //清空batch裏的全部sql
  }
}

(2)小例子:在Oracle數據庫中實現存二進制文件,注意:這些操做需開啓事務。
//1.向列中存一個指針
String sql = "insert into tblob(id,image) values(1,empty_blob())";
PreparedStatement st = conn.prepareStatement(sql);
st.executeUpdate();
//2.把指針查詢出來
sql = "select 二制文件列名 from 表 where id=1 for update";  //for update是爲了鎖定這一行,爲了避免產生併發衝突
st = conn.prepareStatement(sql);
rs = st.executeQuery();
if(rs.next()){
  BLOB blob =  (BLOB) rs.getBlob("image");
  OutputStream out = blob.getBinaryOutputStream();
  URL url = 類.class.getClassLoader().getResource("a.jpg");//獲得文件虛擬地址
  File file = new File(url.getPath()); //獲得文件真實路徑並用file鏈接
  FileInputStream in = new FileInputStream(file);
  byte buffer[] = new byte[1024];
  int len = 0;
  while((len=in.read(buffer))>0){
     out.write(buffer, 0, len);
  }
}
st.executeBatch();

5.處理執行結果
rs.Previous()://移動到前一行
rs.absolute(1)://移動到指定行
rs.beforeFirst():移動ResultSet的最前面(表頭)
rs.afterLast() ://移動到ResultSet的最後面(表尾)
if(rs.next){    //指向第一條,下次執行指向第二行
第一種方法,獲得的是個Object對象
   rs.getObject("id");   //取出該行列名爲id的值
   rs.getObject(1);  //取出該行第一列的值
第二種方法,獲得的是對應類型的值
   rs.getBoolean()       //BIT(數據庫中對應的類型)
   rs.getByte()          //TINYINT
   rs.getShort()         //SMALLINT
   rs.getInt()           //int
   rs.getLong()          //BIGINT
   rs.getString()        //CHAR,VARCHAR,LONGVARCHAR
   //用流方式取出數據庫中的大文本 //Text,longText
   Reader reader = rs.getCharacterStream("大文本列名"或列號);
   或
   Reader reader = rs.getClob("大文本列名"或列號).getCharacterStream();
   char buffer[] = new char[1024];
   int len = 0;
   FileWriter writer = new FileWriter("c:\\存放取出數據的文件名.txt");
   while((len = reader.read(buffer))>0){
      writer.write(buffer,0,len);
   }
   //用流方式取出數據庫中的二進制文件 //Blob,LongBlob
   InputStream in = rs.getBinaryStream("二進制文件存放列名"或列號);
   或
   InputStream in = rs.getBlob("二進制文件存放列名"或列號).getBinaryStream();
   byte buffer[] = new byte[1024];
   int len = 0;
   FileOutputStream out = new FileOutputStream("c:\\1.jpg");
   while((len=in.read(buffer))>0){
       out.write(buffer,0,len);
   }
   rs.getDate()          //DATE,獲得的是java.sql.Date類型
   rs.getTime()          //TIME,獲得的是java.sql.Time類型
   rs.getTimestamp()     //TIMESTAMP,獲得的是java.sql.Timestamp類型
}

6.釋放資源(如下代碼要放在finally中)
rs.close();
st.close();
conn.close();java

 

2、數據分頁顯示(customer工程):mysql

MySql分頁sql語句
select * from customer limit ?,?
Oracle分頁sql語句
select * from(
    select rownum r_, row_.*  from(
select * from student order by id
    )row_ where rownum <=5  //5爲起始索引位置
)where r_>=1  //1爲結束索引位置

1.完成分頁的dao支持
1.獲取總紀錄數的方法
2.根據起始位置,獲得某一頁數據的方法

二、寫響應用戶分頁顯示的servlet
1.調用dao得到總紀錄數
2.獲得用戶查看哪一頁
3.把上面兩個信息,傳遞給page,並使用page對象負責完成計算邏輯
4.根據page計算出來的該頁在數據庫的起始位置,凋用dao得到頁面數據
5.把封裝了頁面數據的list集合,封裝到page對象中
6.把page對象,保存到request域中,交給jsp顯示

三、jsp顯示page對象數據
1.要顯示page裏面封裝的list集合(即頁面數據)
2.foreach循環顯示page裏面封裝的開始頁碼和結束頁碼,以在jsp頁面中顯示頁號

四、page對象完成什麼計算邏輯?
1.要根據總紀錄數,算出總頁數
2.要根據用戶所要查看頁,算出該頁的數據應該從數據庫哪一個位置開始取
3.根據用戶所要查看的頁,計算jsp頁面中,頁號的起始碼和結束碼
4.page對象提供一個list集合,用於封裝頁面數據
修改的作法
1.回顯用戶的數據
1.點修改,訪問servlet:FindCustomer,它負責取出須要修改的用戶的數據
2.FindCustomer這個servlet把用戶數據交給jsp回顯
3.用戶能夠在jsp回顯頁面上修改用戶數據,提交給EditCoustomer,EditCoustomer這個servlet負責用最新的用戶數據,覆蓋原有數據庫的數據sql

 

3、JDBC調用存儲過程步驟:數據庫

1.獲得CallableStatement,並調用存儲過程
CallableStatement cs = conn.prepareCall("{call 方法(?, ?)}");

2.設置參數,註冊返回值,獲得輸出
cs.registerOutParameter(2, Types.VARCHAR);
cs.setString(1, "abc");
cs.execute();
cs.getString(2);apache

 

4、手動寫一個鏈接池數組

1.寫一個自已的類實現java.sql.DataSource接口tomcat

 

5、經常使用數據庫鏈接池(也叫數據源),實現了DataSoruce安全

1.DBCP 是 Apache 軟件基金組織下的開源鏈接池實現
在工程中增長以下兩個 jar 文件:
Commons-dbcp.jar:鏈接池的實現
Commons-pool.jar:鏈接池實現的依賴庫
//dbcpconfig.properties爲DBCP的配置文件
InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);
BasicDataSourceFactory factory = new BasicDataSourceFactory();
DataSource ds = factory.createDateSource(prop);  //獲得數據源對象
ds.getConnection();  //返回Connection,供自已手寫的dao調用
ds.getDataSource();  //DataSource,供DBUtils框架調用

2.C3P0
在工程中增長以下一個 jar 文件:
c3p0-0.9.1.2.jar:鏈接池的實現
ComBolDataSource ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");  //裝載mysql數據庫驅動
ds.setJdbcUrl("jdbc:mysql://localhost:3306/數據庫名"); //註冊
ds.setUser("數據庫用戶名");  
ds.setPassword("數據庫密碼");
ds.setMaxPoolSize(50);  //設置最大鏈接數
ds.setMinPoolSize(10);  //設置最小鏈接數
ds.setInitialPoolSize(20); //設置初始鏈接數
ds.getConnection();  //返回Connection,供自已手寫的dao調用
ds.getDataSource();  //DataSource,供DBUtils框架調用

3.Tomcat的鏈接池
(1)、此種配置下,驅動jar文件需放置在tomcat的lib下
在WebRoot/META-INF/建軍一個context.xml文件,寫以下代碼:
<Context>
  <Resource name="jdbc/任意名稱a"  //爲了讓程序能夠拿到這個配置文件
            auth="Container"
            type="javax.sql.DataSource"
            username="數據庫用戶名"
            password="數據庫密碼"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/數據庫名"
            maxActive="8"
            maxIdle="4"/>
</Context>

(2)、用JNDI(Java Naming and Directory Interface)技術拿到Tomcat的鏈接池:
在javax.naming包下,其核心API爲Context,它表明JNDI容器,其lookup方法爲檢索容器中對應名稱的對象。
Context initCtx = new InitialContext(); //jndi初始化
Context envCtx = (Context) initCtx.lookup("java:comp/env");//獲得tomcat中的JNDI容器
DataSource ds = (DataSource) envCtx.lookup("jdbc/任意名稱a"); //拿到了context.xml中配置的數據庫鏈接池
ds.getConnection();  //返回Connection,供自已手寫的dao調用
ds.getDataSource();  //DataSource,供DBUtils框架調用併發

 

6、事務oracle

1.事務的概念:
事務指邏輯上的一組操做,組成這組操做的各個單元,要不所有成功,要不所有不成功。
數據庫開啓事務命令
start transaction  開啓事務
Rollback  回滾事務
Commit   提交事務
JDBC控制事務語句
//關閉JDBC默認提交方式,讓多條SQL在一個事務中執行
Connection.setAutoCommit(false);  
//設置事務回滾點
Savepoint sp = conn.setSavepoint();
//回滾到上面設置的回滾點
Connection.rollback(sp);
//提交,回滾後必需要提交
Connection.commit();

2.事務的特性(ACID):
原子性(Atomicity):原子性是指事務是一個不可分割的工做單位,事務中的操做要麼都發生,要麼都不發生。?
一致性(Consistency):事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態。
隔離性(Isolation):事務的隔離性是多個用戶併發訪問數據庫時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做數據所幹擾,多個併發事務之間要相互隔離。
持久性(Durability):持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即便數據庫發生故障也不該該對其有任何影響。

3.事務的隔離性:
髒讀:指一個事務讀取了另一個事務未提交的數據。
不可重複讀:在一個事物內讀取表中的某一行數據,屢次讀取結果不一樣。
和髒讀的區別是:髒讀是讀取前一事務未提交的髒數據,不可重複讀是從新讀取了前一事務已提交的數據。
虛讀:是指在一個事務內讀取到了別的事務插入的數據,致使先後讀取不一致。
數據庫共定義了四種隔離級別:
Serializable:可避免髒讀、不可重複讀、虛讀狀況的發生。(串行化)    例:conn.TRANSACTION_SERIALIZABLE
Repeatable read:可避免髒讀、不可重複讀狀況的發生。(可重複讀)
Read committed:可避免髒讀狀況發生(讀已提交)。
Read uncommitted:最低級別,以上狀況均沒法保證。(讀未提交)
數據庫存中對事務隔離性的操做語句:
set   transaction isolation level 設置事務隔離級別
select @@tx_isolation查詢當前事務隔離級別

4.程序中設置隔離級別(conn爲Connection類),此操做要在開啓事務以前:
conn.setTransactionIsolation(conn.TRANSACTION_SERIALIZABLE);
conn.setTransactionIsolation(conn.TRANSACTION_REPEATABLE_READ);
conn.setTransactionIsolation(conn.TRANSACTION_READ_COMMITTED);
conn.setTransactionIsolation(conn.TRANSACTION_READ_UNCOMMITTED);

 

7、元數據-DataBaseMetaData

1.返回數據庫的定義信息:
DataBaseMetaData db = Connection.getDatabaseMetaData():返回數據庫元數據對象
db.getURL():返回一個String類對象,表明數據庫的URL。
db.getUserName():返回鏈接當前數據庫管理系統的用戶名。
db.getDatabaseProductName():返回數據庫的產品名稱。
db.getDatabaseProductVersion():返回數據庫的版本號。
db.getDriverName():返回驅動驅動程序的名稱。
db.getDriverVersion():返回驅動程序的版本號。
db.isReadOnly():返回一個boolean值,指示數據庫是否只容許讀操做。

2.返回表的定義信息:
ParameterMetaData pd = PreparedStatement.getParameterMetaData():返回表明PreparedStatement對象的元數據
pd.getParameterCount():得到PreparedStatement對象傳遞的參數個數
pd.getParameterType(1):得到PreparedStatement對象傳遞的參數中第1個參數的sql類型

3.返回列的定義信息:
ResultSetMetaData rd = ResultSet.getMetaData():得到表明ResultSet對象的元數據對象
rd.getColumnCount():返回resultset對象的列數
rd.getColumnName(1):得到第1列的列名
rd.getColumnTypeName(1):得到第1列的列數據的類型 

 

8、DBUtils框架

共一個包:
org.apache.commons.dbutils
導jar包:commons-dbutils.jar
DbUtils類的方法(都是靜態的):
DbUtils.close(conn,st,rs):關閉Connection、Statement和ResultSet。
DbUtils.closeQuietly(conn,st,rs):關閉Connection、Statement和ResultSet,隱藏異常。
DbUtils.commitAndCloseQuietly(conn): 用來提交鏈接,而後關閉鏈接,而且在關閉鏈接時不拋出SQL異常。 
DbUtils.loadDriver():裝載並註冊JDBC驅動程序,若是成功就返回true,不須要捕捉ClassNotFoundException異常。
QueryRunner類的主要方法:
runner.query(conn,sql,params,ResultSetHandler):查詢操做,將結果集保存到指定JavaBean對象中
runner.query(sql,params,ResultSetHandler):查詢操做,將結果集保存到指定JavaBean對象中
runner.query(conn,sql,ResultSetHandler): 查詢操做,不用參數,將結果集保存到指定JavaBean對象中。
runner.update(conn,sql,params):用來執行一個更新(插入、更新或刪除)操做。
runner.update(conn,sql):用來執行一個不須要置換參數的更新操做。
ResultSetHandler 接口的實現類:
ArrayHandler:把結果集中的第一行數據轉成對象數組。
ArrayListHandler:把結果集中的每一行數據都轉成一個數組,再存放到List中。
BeanHandler:將結果集中的第一行數據封裝到一個對應的JavaBean實例中。
BeanListHandler:將結果集中的每一行數據都封裝到一個對應的JavaBean實例中,存放到List裏。
ColumnListHandler:將結果集中某一列的數據存放到List中。
KeyedHandler(name):將結果集中的每一行數據都封裝到一個小Map裏,再把這些小map再存到一個另外一大map裏,其map的key爲傳進來的key,如name。
MapHandler:將結果集中的第一行數據封裝到一個Map裏,key是列名,value就是對應的值。
MapListHandler:將結果集中的每一行數據都封裝到一個Map裏,而後再存放到List

1.增刪改操做(CUD):
QueryRunner runner = new QueryRunner(DataSource類);  //獲得QueryRunner對象
String sql = "sql語句";  //這裏的語句能夠是CRUD語句
Object[] arr = {參數列表}; //構建參數對象數組
qr.update(sql,arr);   //將參數傳給DBUtils框架,執行增刪改操做

2.經常使用查找操做(R):
User user = runner.query(sql,params,new BeanHandler(user.class)); 
List list = runner.query(sql,params,new BeanListHandler(user.class));

經常使用O-R Mapping映射工具
Hibernate
Ibatis
Commons DbUtils(只是對JDBC簡單封裝)

 

9、CachedRowSet(離線操做數據庫)

1.建立CachedRowSet
CachedRowSetImpl cache = new CachedRowSetImpl();

2.填充CachedRowSet
//對查出的結果集提供分頁功能
cache.setPageSize(10);  //取10行數據
cache.populate(rs,2);  //從第2行開始取,起始值是1

3.更新CachedRowSet調用update***()方法後,調用updateRow方法更新至數據庫,調用acceptChanges方法

相關文章
相關標籤/搜索