jdbc连接数据scanip_java数据库连接_jdbc
1. jdbc 概述
問題:實際開發中,不可能用工具或者命令行操作數據庫,數據庫表中的數據最終要使用Java程序來操作,那么Java中如何操作數據庫中的數據呢?
答 : 在Java語言中,有一個專門連接數據庫的規范(JDBC),專門負責連接數據庫進行數據操作的規范
JDBC只是SUN編寫的一堆接口(規范的體現),SUN公司自己并沒有實現
問題 : 為什么SUN只定義一個JDBC規范,而不實現呢?
答 : 因為市面上的數據庫很多,每個數據庫內部接口不會向外暴露,而且即便是暴露讓SUN去實現,市面上很多數據庫全部要SUN來實現的話就不現實
實際中哪個數據庫需要支持JAVA語言,就需要自己實現Java的JDBC規范,因為實現了JDBC很多接口,那么就會有很多實現類,而很多實現類在java中會使用一個專門的包封裝起來,叫做jar包(在JDBC中叫做驅動包),各大數據庫產商實現JDBC規范以后都會把他們jar包放在官網上以供開發者下載使用
1.1 JDBC
JDBC(Java DataBase Connectivity): 是一種用于執行SQL語句的Java API,可以為多種關系數據庫提供統一訪問,它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基于JDBC規范對應的API包
2. 入門案例
2.1 連接數據庫
案例使用JDBC操作MySQL數據庫
2.2 創建普通java項目
2.3 在項目下面新建一個lib目錄
2.4 將MySQL驅動包拷貝到項目中并添加依賴
2.5 獲取數據庫連接對象
準備:
1.拷貝MySQL的驅動包到項目中去:mysql-connector-java-5.1.x-bin.jar
2.build path,告速項目去哪里去找字節碼文件.
--------------------------------------------------------------------------------
操作JDBC的第一步,獲取JDBC的連接對象.:Connection.
步驟:
1.加載注冊驅動.
就是把驅動中的Driver字節碼加載到JVM中.
Class.forName("com.mysql.jdbc.Driver");
為什么這句話就可以加載注冊驅動?
第一步:把com.mysql.jdbc.Driver.class這份字節碼加載到JVM中.
第二步:當一份字節碼被加載進JVM,馬上就會執行該字節碼中的靜態代碼塊.
第三步:該靜態代碼中,就在完成,先創建驅動對象,再注冊.
2.通過DriverManager獲取連接對象.
public static Connection getConnection(String url,String user,String password)
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbName","root","admin");
jdbc:mysql://localhost:3306/dbName
jdbc:mysql:// :連接MySQL數據庫的協議,不同數據庫協議不一樣
localhost:3306 :數據庫軟件的主機和端口
dbName : 具體要連接數據庫
若數據庫安裝在本機,并且端口是默認的3306,則可以簡寫:
Connection conn= DriverManager.getConnection("jdbc:mysql:///dbName","root","admin");
驗證已經獲取連接:可以在MySQL控制臺,使用命令:show processlist; 查看MySQL運行進程。
public class GetConnectionDemo {public static void main(String[] args) throws Exception {//1.加載注冊驅動 : 把當前類對應的字節碼加載到JVM中Class.forName("com.mysql.jdbc.Driver");//2.獲取數據庫連接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");System.out.println(conn);} }3. 創建表-DDL操作
在其他操作之間先要把數據庫表要創建出來
/*創建一張t_student表: id: name: age: *//** * 創建表操作* SQL : create table t_student (id int primary key auto_increment,name varchar(50),age int)*/ public static void main(String[] args) throws Exception {String sql = "create table t_student (id int primary key auto_increment,name varchar(50),age int)";//賈璉欲執事//1,加載注冊驅動Class.forName("com.mysql.jdbc.Driver");//2,獲取數據庫連接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");//3,創建語句對象(用于執行SQL語句的對象)Statement st = conn.createStatement();//4, 執行SQL語句//int rows = st.executeUpdate(String sql);執行DDL和DML語句,返回的是受影響的行數//ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象st.executeUpdate(sql);//5,釋放資源(先開后關)st.close();conn.close();}4. DML操作-表數據的增刪改
//DML : 對表數據的增刪改操作 public class DMLDemo {/** 向 t_student表中插入一條數據 * sql : insert into t_student(name,age) values ('喬峰',30)*/@Testpublic void testInsert() throws Exception {String sql = "insert into t_student(name,age) values ('喬峰',30)";// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建語句對象Statement st = conn.createStatement();// 4.執行SQL語句// int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數// ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象int rows = st.executeUpdate(sql);System.out.println(rows);//5.釋放資源(先開后關)st.close();conn.close();}/** 刪除操作: 刪除t_student表中的某一條數據* SQL :delete from t_student where id = 2*/@Testpublic void testDelete() throws Exception {String sql = "delete from t_student where id = 2";// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建語句對象Statement st = conn.createStatement();// 4.執行SQL語句// int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數// ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象int rows = st.executeUpdate(sql);System.out.println(rows);//5.釋放資源(先開后關)st.close();conn.close();}/** 修改操作 : 修改t_student表中的某一條數據* SQL : update t_student set name = '虛竹',age = 50 where id = 3*/@Testpublic void testUpdate() throws Exception {String sql = "update t_student set name = '虛竹',age = 50 where id = 3";// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建語句對象Statement st = conn.createStatement();// 4.執行SQL語句// int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數// ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象int rows = st.executeUpdate(sql);System.out.println(rows);//5.釋放資源(先開后關)st.close();conn.close();} }5. DQL操作-查詢操作
5.1 查詢操作的分析
5.2 查詢具體操作
結果集的列的位置
1. 使用 rs.next() 偏移光標,循環指定具體的某一行
獲取數據的具體方法
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.ArrayList; import java.util.List;import org.junit.Test;//DQL :查詢操作 public class D_DQLDemo {/** 多行查詢 :查詢t_student表中的所有數據* SQL : select * from t_student*/@Testpublic void testList() throws Exception {String sql = "select * from t_student";// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建語句對象Statement st = conn.createStatement();// 4.執行SQL語句// int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數// ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象ResultSet rs = st.executeQuery(sql);//創建list集合用于封裝Student對象List<Student> stus = new ArrayList<>();while(rs.next()) {//1.通過結果集的位置獲取對應的數/*Object id = rs.getObject(1);Object name = rs.getObject(2);Object age = rs.getObject(3);*///2.通過結果集的 列名獲取對應的數據/*Object id = rs.getObject("id");Object name = rs.getObject("name");Object age = rs.getObject("age");*///3.通過數據庫數據和Java對應的數據類型獲取對應的只int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//將獲取的數據封裝成對應的Student對象Student stu = new Student(id, name, age);//將一個個Student對象添加到list集合中stus.add(stu);}for (Student student : stus) {System.out.println(student);}//5.釋放資源(先開后關)rs.close();st.close();conn.close();}/** 單行查詢: 查詢出t_student 指定id的信息* SQL : select * from t_student where id = 1;*/@Testpublic void testGetOne() throws Exception {String sql = "select * from t_student where id = 2";// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建語句對象Statement st = conn.createStatement();// 4.執行SQL語句// int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數// ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象ResultSet rs = st.executeQuery(sql);if(rs.next()) {//1.通過結果集的位置獲取對應的數/*Object id = rs.getObject(1);Object name = rs.getObject(2);Object age = rs.getObject(3);*///2.通過結果集的 列名獲取對應的數據/*Object id = rs.getObject("id");Object name = rs.getObject("name");Object age = rs.getObject("age");*///3.通過數據庫數據和Java對應的數據類型獲取對應的只int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//將獲取的數據封裝成對應的Student對象Student stu = new Student(id, name, age);System.out.println(stu);}//5.釋放資源(先開后關)rs.close();st.close();conn.close(); } }6. 預編譯語句對象PreparedStatment
問題 : 我們有了Statment對象可以執行SQL,為什么還要使用PreparedStatment?
優勢
1. SQL語句結構清晰,參數的設置和SQL語句分離
2. 性能更高
3. 防止SQL注入
Statement: 表示靜態SQL語句對象.
PreparedStatement:Statement的子接口,表示預編譯SQL語句對象. 通過占位符(?)來拼SQL.
6.1 創建PreparedStatement
創建語句對象 Statment
6.2 執行SQL語句的方法
6.2.1 Statement
在執行SQL語句的時候會帶上SQL語句
6.2.2 PreparedStatemeny
在執行SQL語句的方法中不需要設置SQL語句
6.3 設置站位參數的值
void setXxx(int parameterIndex,Xxx value):用于設置占位符參數,
parameterIndex:第幾個問號. 注意:從1開始.
value:設置的真實值.
Xxx:表示數據類型.String/int/long/Double/Date
6.4 代碼
import static org.junit.Assert.*;import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement;import org.junit.Test;//DML : 對表數據的增刪改操作,使用預編譯語句對象 public class E_DMLByPreparedStatmentDemo {/** 向 t_student表中插入一條數據 * sql : insert into t_student(name,age) values ('喬峰',30)*/@Testpublic void testInsert() throws Exception {String sql = "insert into t_student(name,age) values (?,?)";// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建預編譯語句對象PreparedStatement ps = conn.prepareStatement(sql);//3.1設置占位符參數ps.setString(1, "東方姑娘");ps.setInt(2, 18);// 4.執行SQL語句:注意不要帶SQL參數ps.executeUpdate();//5.釋放資源(先開后關)ps.close();conn.close();}/** 刪除操作: 刪除t_student表中的某一條數據* SQL :delete from t_student where id = 2*/@Testpublic void testDelete() throws Exception {String sql = "delete from t_student where id = ?";// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建預編譯語句對象PreparedStatement ps = conn.prepareStatement(sql);//3.1設置占位符對應的參數值ps.setInt(1, 1);// 4.執行SQL語句int rows = ps.executeUpdate();System.out.println(rows);//5.釋放資源(先開后關)ps.close();conn.close();}/** 修改操作 : 修改t_student表中的某一條數據* SQL : update t_student set name = '虛竹',age = 50 where id = 3*/@Testpublic void testUpdate() throws Exception {String sql = "update t_student set name = ?,age = ? where id = ?";// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建預編譯語句對象PreparedStatement ps = conn.prepareStatement(sql);//3.1設置占位符參數對應的值ps.setString(1, "西方失敗");ps.setInt(2, 40);ps.setInt(3, 4);// 4.執行SQL語句int rows = ps.executeUpdate();System.out.println(rows);//5.釋放資源(先開后關)ps.close();conn.close();} }7. JavaWeb開發的分層設計-三層架構
實際開發中,JavaWeb開發代碼一般分為三層,分層結構是JavaWeb開發中的一種設計思想,這樣會讓我們開發層次分明,每一層只要完成對應的功能即可,使得項目便于開發和維護
1 . Web層/表現層 : 主要接受前臺瀏覽器用戶的參數,給瀏覽器響應數據等等
2. Service層/業務成/服務層:主要處理業務功能,日志,權限,事物,等等
3. DAO層/持久層 :專門負責和數據庫交互,數據處理相關代碼
DAO : Data Access Object 數據訪問對象
實際開發中 : 用戶請求到-Web層--->Service層-->DAO層
7.2 DAO思想
7.3 使用DAO以后代碼的以及包的設計結構
開發中如果使用的分層,編寫的包和類名接口名等等都是有固定規則,不能隨便瞎寫
7.3.1 DAO層接口包命名
公司域名倒寫+項目名稱/模塊名稱+dao
如 : cn.sxt.crm.dao
7.3.2 DAO層實現類包命名
公司域名倒寫+項目名稱/模塊名稱+dao+impl
如 : cn.sxt.crm.dao.impl
7.3.3 DAO層操作對應表的接口命名
對應表的名稱 + Dao/DAO
如 : StudentDao/DAO , TeacherDao/DAO
7.3.4 DAO層操作對應表的實現類命名
對應表的名稱 + Dao/DAOImpl
如 : StudentDaoImpl/DAOImpl , TeacherDaoImpl/DAOImpl
7.3.5 數據表對應的Java類domain/pojo包命名
POJO(Plain Ordinary Java Object)簡單的Java對象
domian : 域對象
公司域名倒寫+項目名稱/模塊名稱+domain/pojo
如 : cn.sxt.crm.domain
7.3.6 對應的測試包命名
公司域名倒寫+項目名稱/模塊名稱+test
如 : cn.sxt.crm.test
7.3.7 項目的工具類包命名
公司域名倒寫+項目名稱/模塊名稱+util/utils
如 : cn.sxt.crm.util/utils
7.3.8 DAO代碼設計結構
7.3.9 Dao的實現類代碼
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List;import cn.sxt.jdbc.dao.StudentDao; import cn.sxt.jdbc.domain.Student;public class StudentDaoImpl implements StudentDao {@Overridepublic int saveStudent(Student stu) {String sql = "insert into t_student(name,age) values (?,?)";Connection conn = null;PreparedStatement ps = null;try {// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建預編譯語句對象ps = conn.prepareStatement(sql);//3.1設置占位符參數ps.setString(1, stu.getName());ps.setInt(2, stu.getAge());// 4.執行SQL語句:注意不要帶SQL參數return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {//5.釋放資源(先開后關)try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}return 0;}@Overridepublic int deleteById(int id) {String sql = "delete from t_student where id = ?";Connection conn = null;PreparedStatement ps = null;try {// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建預編譯語句對象ps = conn.prepareStatement(sql);//3.1設置占位符參數ps.setInt(1, id);// 4.執行SQL語句:注意不要帶SQL參數return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {//5.釋放資源(先開后關)try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}return 0;}@Overridepublic int updateStudentById(Student stu) {String sql = "update t_student set name = ?,age = ? where id = ?";Connection conn = null;PreparedStatement ps = null;try {// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建預編譯語句對象ps = conn.prepareStatement(sql);//3.1設置占位符參數ps.setString(1, stu.getName());ps.setInt(2, stu.getAge());ps.setInt(3, stu.getId());// 4.執行SQL語句:注意不要帶SQL參數return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {//5.釋放資源(先開后關)try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}return 0;}@Overridepublic Student selectById(int id) {String sql = "select * from t_student where id = ?";Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建語句對象ps = conn.prepareStatement(sql);//3.1設置占位符參數對應的值ps.setInt(1, id);// 4.執行SQL語句rs = ps.executeQuery();if(rs.next()) {//通過數據庫數據和Java對應的數據類型獲取對應的只String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//將獲取的數據封裝成對應的Student對象Student stu = new Student(id, name, age);return stu;}} catch (Exception e) {// TODO: handle exception}finally {try {if(rs !=null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}}return null;}@Overridepublic List<Student> selectList() {String sql = "select * from t_student";//創建list集合用于封裝Student對象List<Student> stus = new ArrayList<>();Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {// 1.加載注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2.獲取數據庫連接對象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.創建語句對象ps = conn.prepareStatement(sql);// 4.執行SQL語句rs = ps.executeQuery();while(rs.next()) {//通過數據庫數據和Java對應的數據類型獲取對應的只int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//將獲取的數據封裝成對應的Student對象Student stu = new Student(id, name, age);//將一個個Student對象添加到list集合中stus.add(stu);}} catch (Exception e) {// TODO: handle exception}finally {try {if(rs !=null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}}return stus;}}7.3.10 快速生成單元測試類
一個dao層或者service編寫代碼以后,需要為每一個功能都進行單元測試,一個dao中的方法很多。我們快速為這個dao層的類生成單元測試類,(dao的每一個方法都自動生成一個測試方法)
7.4 代碼初步重構
上述的DAO方法中的代碼,存在的問題:
問題1:每個DAO方法中都會寫:驅動名稱/url/賬號/密碼,不利于維護.
解決方案: 聲明為成員變量即可.(在被類中任何地方都可以訪問)
問題2:問題1的解決方案有問題.
每個DAO實現類里都有一模一樣的4行代碼,不利于維護(考慮有100個DAO實現類,就得重復99次).
解決方案: 把驅動名稱/url/賬號/密碼這四行代碼,專門抽取到一個JDBC的工具類中.---->JdbcUtil.
問題3:其實DAO方法,每次操作都只想需要Connection對象即可,而不關心是如何創建的.
解決方案:把創建Connection的代碼,抽取到JdbcUtil中,并提供方法getConn用于向調用者返回Connection對象即可.
問題4:每次調用者調用getConn方法的時候,都會創建一個Connection對象.
但是,每次都會加載注冊驅動一次.--->沒必要的.
解決方案:把加載注冊驅動的代碼放在靜態代碼塊中--->只會在所在類被加載進JVM的時候,執行一次.
問題5:每個DAO方法都要關閉資源.(雞肋代碼).
解決方案:把關閉資源的代碼,抽取到JdbcUtil中.
public static void close(Connection conn, Statement st, ResultSet rs) {}
調用者:
DML: JdbcUtil.close(conn,st,null);
DQL: JdbcUtil.close(conn,st,rs);
問題6 :連接數據庫的賬號密碼寫死在JdbcUtil工具類中了,不利于維護
抽取 db.properties 配置文件,將數據庫對應的賬號密碼寫到配置文件中,然后使用程序讀取配置文件內容即可
7.4.1 jdbcUtil 工具類
import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties;public class JdbcUtil {// alt+shif+a 多行修改,修改以后還原 alt+shif+a/*private static String driverClassName = "com.mysql.jdbc.Driver";private static String url = "jdbc:mysql://localhost:3306/jdbcdemo";private static String username = "root";private static String password = "root";*/private static Properties p = new Properties();static {try {//1.獲取類加載器ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//2,使用類加載器獲取項目 類路徑下面的文件InputStream inputStream = classLoader.getResourceAsStream("db.properties");//3.使用Priperties加載配置文件對應的輸入流p.load(inputStream);Class.forName(p.getProperty("driverClassName"));} catch (Exception e) {e.printStackTrace();}}public static Connection getConnection() {try {return DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password"));} catch (Exception e) {e.printStackTrace();throw new RuntimeException("親,連接數據庫失敗", e);}}public static void close(Connection conn,PreparedStatement ps,ResultSet rs) {try {if(rs !=null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}} }7.4.2 使用工具類以后的DAO實現類效果
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List;import cn.sxt.jdbc.dao.StudentDao; import cn.sxt.jdbc.domain.Student; import cn.sxt.jdbc.util.JdbcUtil;public class StudentDaoImpl implements StudentDao {@Overridepublic int saveStudent(Student stu) {String sql = "insert into t_student(name,age) values (?,?)";Connection conn = null;PreparedStatement ps = null;try {conn = JdbcUtil.getConnection();// 3.創建預編譯語句對象ps = conn.prepareStatement(sql);//3.1設置占位符參數ps.setString(1, stu.getName());ps.setInt(2, stu.getAge());// 4.執行SQL語句:注意不要帶SQL參數return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {JdbcUtil.close(conn, ps, null);}return 0;}@Overridepublic int deleteById(int id) {String sql = "delete from t_student where id = ?";Connection conn = null;PreparedStatement ps = null;try {conn = JdbcUtil.getConnection();// 3.創建預編譯語句對象ps = conn.prepareStatement(sql);//3.1設置占位符參數ps.setInt(1, id);// 4.執行SQL語句:注意不要帶SQL參數return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {JdbcUtil.close(conn, ps, null);}return 0;}@Overridepublic int updateStudentById(Student stu) {String sql = "update t_student set name = ?,age = ? where id = ?";Connection conn = null;PreparedStatement ps = null;try {conn = JdbcUtil.getConnection();// 3.創建預編譯語句對象ps = conn.prepareStatement(sql);//3.1設置占位符參數ps.setString(1, stu.getName());ps.setInt(2, stu.getAge());ps.setInt(3, stu.getId());// 4.執行SQL語句:注意不要帶SQL參數return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {JdbcUtil.close(conn, ps, null);}return 0;}@Overridepublic Student selectById(int id) {String sql = "select * from t_student where id = ?";Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JdbcUtil.getConnection();// 3.創建語句對象ps = conn.prepareStatement(sql);//3.1設置占位符參數對應的值ps.setInt(1, id);// 4.執行SQL語句rs = ps.executeQuery();if(rs.next()) {//通過數據庫數據和Java對應的數據類型獲取對應的只String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//將獲取的數據封裝成對應的Student對象Student stu = new Student(id, name, age);return stu;}} catch (Exception e) {// TODO: handle exception}finally {try {if(rs !=null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}finally {JdbcUtil.close(conn, ps, rs);}}return null;}@Overridepublic List<Student> selectList() {String sql = "select * from t_student";//創建list集合用于封裝Student對象List<Student> stus = new ArrayList<>();Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JdbcUtil.getConnection();// 3.創建語句對象ps = conn.prepareStatement(sql);// 4.執行SQL語句rs = ps.executeQuery();while(rs.next()) {//通過數據庫數據和Java對應的數據類型獲取對應的只int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//將獲取的數據封裝成對應的Student對象Student stu = new Student(id, name, age);//將一個個Student對象添加到list集合中stus.add(stu);}} catch (Exception e) {// TODO: handle exception}finally {JdbcUtil.close(conn, ps, rs);}return stus;}}7.5 知識點補充,類加載器
在項目的 類路徑(src)下面創建一個 db.properties配置文件,專門配置連接數據庫的賬號密碼
如何使用類加載器加載配置文件
7.5.1 配置文件
7.5.1.1 配置文件創建的位置
配置文件一般都放在項目的src 源目錄下面
7.5.2 加載代碼
import static org.junit.Assert.*; import java.io.InputStream; import java.util.Properties; import org.junit.Test;public class PropertiesTest {@Testpublic void testName() throws Exception {/** ClassLoader 類加載器* ClassLoader :可以從項目的類路徑下面讀取對應的配置文件返回一個輸入流* ClassLoader 在程序運行的時候JVM已經為每一個項目都創建了一個,我們開發者只需要獲取即可* 獲取類加載器方式* 1、使用當前線程* ClassLoader classLoader = Thread.currentThread().getContextClassLoader();* 2、通過某一類的字節碼實例也可以獲取* ClassLoader classLoader = PropertiesTest.class.getClassLoader();*/ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//使用類加載器獲取項目 類路徑下面的文件InputStream inputStream = classLoader.getResourceAsStream("db.properties");/** Properties 是Map集合下面的一個 專門用于讀取配置文件的對象* 可以讀取當前類路徑下面的 xxx.properites類型的配置文件* * xxx.properites的內容必須是key=value 鍵值對的數據*///1.創建Properties對象Properties p = new Properties();//2.加載配置文件p.load(inputStream);System.out.println(p);//獲取具體某一個key對應的值String driverClassName = p.getProperty("driverClassName");System.out.println(driverClassName);} }7.5.3 效果
8. 連接池
8.1 遇到的問題-引出連接池
8.2 連接池思想
8.3 連接池的概述
在Java中,連接池使用javax.sql.DataSource接口來表示連接池.
注意:DataSource僅僅只是一個接口,由各大服務器廠商來實現(Tomcat.JBoss,阿里巴巴).
常用的DataSource的實現:
DBCP: Spring推薦的
C3P0: Hibernate推薦的
Druid : (德魯伊)阿里巴巴開源的,性能最好,速度最快
DataSource(數據源)和連接池(Connection Pool)是同一個。
8.4 使用連接池和不使用連接池的區別在哪里
從代碼上:
不使用連接池: Conenction對象由DriverManager獲取.
Connection conn = DriverManager.getConnection(url,username,password);
使用連接池:
如何創建DataSource對象,如何在DataSource中設置url,賬號,密碼.
Connection conn = DataSource對象.getConnection();
--------------------------------------------------------------------
使用連接池的時候:
釋放資源: Connection對象.close():
是把Connection放回給連接池,而不是和數據庫斷開.
8.5 Druid連接池的使用
8.5.1 準備druid 連接池jar包到項目
import static org.junit.Assert.*;import java.io.InputStream; import java.io.Reader; import java.sql.Connection; import java.util.Properties;import javax.sql.DataSource;import org.junit.Test;import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; import com.alibaba.druid.pool.DruidPooledConnection;public class DataSourceTest {// 直接創建連接池對象@Testpublic void testName() throws Exception {// 1.創建連接池對象DruidDataSource ds = new DruidDataSource();// 2.設置連接數據庫的賬號密碼ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/jdbcdemo");ds.setUsername("root");ds.setPassword("root");ds.setMaxActive(10);// 最大連接數// 3.獲取連接對象Connection conn = ds.getConnection();System.out.println(conn);}// 使用工廠對象創建連接池對象,工廠對象的好處,不需要直接設置賬號密碼等等,只需要將// 連接數據庫的賬號密碼等等以指定的 key的名稱配置到 xxx.properties文件中即可,工廠對象底層自動讀取@Testpublic void testDataSourceByFactory() throws Exception {// 1.獲取類加載器用于加載clsspath下面的 配置文件ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 2.讀取druid.properties配置文件InputStream inputStream = classLoader.getResourceAsStream("druid.properties");// 3.創建Properties對象,并讀取配置文件對應的輸入流Properties p = new Properties();p.load(inputStream);// 4.創建連接池對象DataSource ds = DruidDataSourceFactory.createDataSource(p);// 5.獲取連接對象Connection conn = ds.getConnection();System.out.println(conn);} }8.5.2 db.propperties
8.5.3 使用Druid抽取的工具類
9. 事務
案例:銀行轉賬:從張無忌賬戶上給趙敏轉1000塊.
準備:account(賬戶表):
---------------------------------------------------------------
id name(賬號,唯一) balance(余額)
1 張無忌 20000
2 趙敏 0
---------------------------------------------------------------
轉賬的思路:
1.檢查張無忌的賬號余額是否大于等于1000.
SQL: SELECT balance FROM account WHERE name = '張無忌' AND balance >=1000
余額>=1000:GOTO 2:
余額 <1000:提示:親,你的余額不足.
2.在張無忌的賬號余額上減少1000.
SQL: UPDATE account SET balance = balance-1000 WHERE name = '張無忌'
3.在趙敏的賬戶余額尚增加1000.
SQL: UPDATE account SET balance = balance+1000 WHERE name = '趙敏'
-------------------------------------------------------------------------------------------
注意:在第二步和第三步之間,停電了.
使用異常模擬停電:System.out.println(1/0);
9.1 事務概述
事務(Transaction,簡寫為tx):
在數據庫中,所謂事務是指一組邏輯操作單元,使數據從一種狀態變換到另一種狀態。
為確保數據庫中數據的一致性,數據的操縱應當是離散的成組的邏輯單元:
當每個邏輯操作單元全部完成時,數據的一致性可以保持,
而當這個單元中的一部分操作失敗,整個事務應全部視為錯誤,所有從起始點以后的操作應全部回退到開始狀態。
事務的操作:先定義開始一個事務,然后對數據作修改操作,這時如果提交(commit),這些修改就永久地保存下來,如果回退(rollback),數據庫管理系統將放棄您所作的所有修改而回到開始事務時的狀態。
--------------------------------------------------
事務的ACID屬性:
1. 原子性(Atomicity)
原子性是指事務是一個不可分割的工作單位,事務中的操作要么都發生,要么都不發生。
2. 一致性(Consistency)
事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。(數據不被破壞)
3. 隔離性(Isolation)
事務的隔離性是指一個事務的執行不能被其他事務干擾,即一個事務內部的操作及使用的數據對并發的其他事務是隔離的,并發執行的各個事務之間不能互相干擾。
4. 持久性(Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來的其他操作和數據庫故障不應該對其有任何影響
--------------------------------------------------
事務:指構成單個邏輯工作單元的操作集合
事務處理:保證所有事務都作為一個工作單元來執行,即使出現了故障,都不能改變這種執行方式。當在一個事務中執行多個操作時,要么所有的事務都被提交(commit),要么整個事務回滾(rollback)到最初狀態
處理事務的兩個動作:
提交:commit: 當整個事務中,所有的邏輯單元都正常執行成功. ---->提交事務.---數據已經提交,不能更改.
回滾:rollback: 當整個事務中,有一個邏輯單元執行失敗, ---->回滾事務.
撤銷該事務中的所有操作--->恢復到最初的狀態.
---------------------------------------------------------------------------------------------------
如何在代碼中去處理事務:
1.在JDBC中,事務是默認自動提交的. 必須先設置事務為手動提交.
connection對象.setAutoCommit(false);//設置事務為手動提交.
2.手動的提交事務.
connection對象.commit();
3.若出現異常必須回滾事務:
不回滾事務,總余額依然是正確的. 若不回滾事務,不會釋放數據庫資源.
connection對象.rollback();
-----------------------------------------------------------------------------------
1.在JDBC在事務是默認提交的,那是在什么時候提交的.
在執行一個DML/DDL操作的時候,就已經提交事務了.
2.針對于CRUD操作. 只有DML操作才有事務,查詢操作沒有事務.
但是,我們一般會把查詢也放在事務里面.
4. 以后,凡是發現自己編寫的代碼是正確的,測試也通過,但是就是數據庫表中的數據不變----->事務沒提交的問題.
4.MySQL中,InnoDB支持外鍵.支持事務,MyISAM不支持外鍵,不支持事務.
InnoDB存儲引擎: 支持事務,支持外鍵,但是查詢效率略低,(金融,理財,p2p)
MyISAM存儲引擎:不支持事務和外鍵,但是查詢效率較高(新聞網站)
Oracle 不存在存儲引擎,都有事務
9.2 事務處理代碼
public class TransactionTest {@Testpublic void testName() throws Exception {Connection conn = null;Statement st = null;ResultSet rs = null;try {conn = DruidUtil.getConnection();//將事務設置為手動提交conn.setAutoCommit(false);st = conn.createStatement();// 1.檢查張無忌的賬號余額是否大于等于1000.rs = st.executeQuery("SELECT balance FROM account WHERE name = '張無忌' AND balance >=1000");if(!rs.next()) {throw new RuntimeException("親,您的賬戶余額不夠");}// 余額>=1000:GOTO 2:// 余額 <1000:提示:親,你的余額不足.// 2.在張無忌的賬號余額上減少1000.st.executeUpdate("UPDATE account SET balance = balance-1000 WHERE name = '張無忌'");System.out.println(1/0);// 3.在趙敏的賬戶余額尚增加1000.st.executeUpdate("UPDATE account SET balance = balance+1000 WHERE name = '趙敏'");//提交事務conn.commit();} catch (Exception e) {e.printStackTrace();//回滾事務conn.rollback();}finally {DruidUtil.close(conn, st, rs);}} }總結
以上是生活随笔為你收集整理的jdbc连接数据scanip_java数据库连接_jdbc的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 库 python_20个必不可少的Pyt
- 下一篇: 脚本升级_Openssh升级脚本