(Oracle真的是走哪禍害到哪23333)javascript
Java多用MySQL和Oraclejava
SQLServer也收費,可是還行,比Oracle便宜,一個差很少3w多python
SQLite被嵌入到了安卓系統中,主要用於安卓開發,是徹底免費的mysql
關於MySQL收費的問題,這篇文章說的很透:git
你使用開源軟件並不受GPL約束,只有在你基於開源軟件,修改開源軟件的源碼的時候才受 GPL約束。MySQL做爲一個開源數據庫,幾乎全部的用戶都只是經過本身的程序去操做這個數據庫,不涉及到改動源碼的問題,根本不用去考慮是否要遵循 GPL的問題。只有在你修改MySQL源碼的狀況下,才須要考慮GPL。github
若是你只是使用MySQL而不是改寫MySQL,那麼在這些狀況下你應該考慮購買Oracle的商業版本,一是Oracle的商用版本提供的附加組件(監控器、備份工具等)對你有價值,二是Oracle的年度技術支持是你須要的,三是各類潛規則。而不該該是你想合法的使用MySQL纔去購買其商業版本。另外,若是你是基於MySQL的源碼開發你本身的產品,那麼你須要購買的是商業受權,而不是subscription這些商業版本。sql
除了以上狀況,使用社區版就好shell
關閉服務須要使用管理員權限數據庫
服務器沒法鏈接時,要檢查服務是否正常開始了數組
登陸MySQL:
mysql -uroot -proot
//u表明user,後面的root是用戶名,p表明password,後面的root是密碼
退出:exit/quit
登陸其餘電腦的MySQL
mysql -h127.0.0.1 -uroot -proot//這裏的127.0.0.1應該替換爲對應的服務器ip地址
也能夠這樣:
mysql --hosst=ip --user=root --password=root
bin:二進制可執行文件
include:C語言的頭信息
lib:運行所須要的庫文件
share:錯誤記錄
這個目錄在C盤隱藏的文件夾ProgramData裏面
my.ini是MySQL的配置文件
數據存放的位置:
MySQL shell:mysql-shell是一個高級的mysql命令行工具、它直接兩種模式(交互式&批處理式)三種語言(javascript\python\sql),格式爲JSON。MySQL是在官方版本5.7.12推出,工具的初衷自己都是爲了解決一類問題,想必官方從不少方面瞭解到工具的使用狀況,支持的開發語言太多,衆口難調,因此這麼個命令行工具就出來了
日常對數據庫的操做使用Command Line Client其實就行了
註釋必定要帶空格,命令都要以分號結尾
------
information_schema:描述數據庫的信息,存放的是視圖而不是表,不存在真正對應的物理文件
mysql:核心數據庫,存放表文件
performance_schema:對性能提高作操做
上面的三個數據庫都要求不要貿然修改,不然可能會影響數據庫穩定性
(查詢數據庫的建立語句,能夠查看數據庫的字符集)
不能建立重名數據庫
注意,建立表的時候,對於表名和Entity的名稱用的是`,而不是單引號
先判斷,再建立
選擇使用特定字符集的數據庫
總結,關於數據庫的建立:
若是數據庫不存在,刪除會報錯,能夠先判斷是否存在再刪除
注意應該寫utf8而不是utf-8
數據類型:
這裏要認識一個單詞:alter(修改)
修改列明部分,應該是「新列名」而不是「新列別」
引號單雙均可以
TRYNCATE是直接所有刪除,而delete是一條一條刪,TRUNCATE效率更高
null參與的運算結果都是null(null就是unkown,計算結果固然也是unkown了)
起別名:
注意這裏的等號是一個的,不要寫成==
<>和!=同樣,是不等於的意思
&& 和 AND等效
NULL不能用等號去判斷,只能用is
NOT放在IS後面
查詢優化:
注意,這個操做對於不一樣的軟件不同,這裏只是MySQL中的方法
DBA:數據庫管理員,對數據庫專門負責
mysql這個數據庫是不顯示在workbeach上的
題外話:在SQL指令中,若是隻想執行script中的某一條或者某幾條指令,應該用鼠標將其選起來,這樣其餘指令就不會執行了
注意,%通配符在主機名這一欄也是可使用的,能夠用來表示全部主機。
修改密碼:
注意數據庫中的密碼是加密的,在查看時候也看不到原密碼,修改密碼時須要使用PASSWORD()函數來對要設置的密碼進行加密
root用戶的密碼忘記了也有方案!
惟一約束並不要求NULL惟一
主鍵:非空且惟一,惟一標識
基於主鍵約束的自動增加
MySQL 每張表只能有1個自動增加字段,這個自動增加字段便可做爲主鍵,也能夠用做非主鍵使用,可是請注意將自動增加字段當作非主鍵使用時必須爲其添加惟一索引,不然系統將會報錯。例如:
– 將自動增加字段設置爲主鍵
create table t1 (id int auto_increment Primary key,sid int);
– 將自動增加字段設置爲非主鍵,注意必須顯式添加Unique鍵
create table t2 (sid int primary key,id int auto_increment Unique);
– 將自動增加字段設置爲非主鍵若是未添加惟一索引將會報錯,以下面語句
create table t3 (sid int primary key,id int auto_increment);
這裏放在主鍵裏面講,只是由於大多數狀況下自動增加都是用在主鍵上面的
自動增加只和上一條有關係,若是人工插入改變了序號,則下一條會接着上一條進行
設置了自動增加的列通常輸入NULL就能夠了,輸入具體數字會改變原有排序
表之間的關聯,能夠用來消除冗餘數據。
通常外鍵都關聯主表的主鍵
被關聯的表必定要先於主動進行管理的表完成建立
被關聯的表在引用解除以前不能被刪除,要刪除或者修改就必須先解除引用,解除引用不必定是指要刪除外鍵,將調用該對象的那個鍵指向其餘或者是NULL就行了
外鍵約束會自動要求必須存在,但能夠爲NULL
注意,外鍵添加在表建立的末尾,而且會再起一個外鍵名(這裏涉及到了三個名字),以後的刪除也是刪除這種約束自己,而不是刪除被加在其上的列
外鍵的級聯:能夠更方便地修改被調用的鍵值,更改會直接體如今被調用表上
級聯包括更新和刪除兩個方面
級聯刪除有點危險,並且效率不高
因此,實際開發中,級聯的使用是很謹慎的
表是現實的抽象
數據庫設計涉及到多表關係和範式(數據庫設計準則)兩個方面
一對多:在多的一方創建外鍵,指向一的一方的主鍵
多對多:創建中間表,中間表至少有兩個字段,分別指向兩張表的主鍵(聯合主鍵)
一對一:一對一關係的實現,能夠在任意一方添加惟一外鍵指向另外一方的主鍵
? 可是!通常狀況下,若是遇到一對一的關係,通常狀況下都會直接合成一張表而不是兩張表
如何在workbeach中生成EER圖
>
---
越高範式冗餘越小
通常來講,遵照了前三種範式,數據庫設計就沒有太大問題了
第N範式都是以遵循前面的範式爲基礎的,因此,範式分析都是從前至後的
每一列都是不可分割的原子數據項
這種是不符合第一範式的
修改成沒有複合列的狀況就行了
上面的表格存在的問題:
例如上面的例子,(學號,課程名稱)稱爲組,這兩個屬性也被稱爲主屬性,分數這個非主屬性是徹底依賴於組的,而姓名、系名和系主任這三個非主屬性只依賴於學號,這就不符合第二範式了。
如何修改?拆分表
A表中,主屬性是學號和課程名稱;B表中,主屬性是學號
依然沒有解決的問題:
例如上面的B表中,姓名被系名依賴,而系名被系主任依賴,這就是傳遞依賴
解決方法?再分表
剛剛全部的問題就所有解決了
通常地,每一天咱們都須要對數據進行備份
多表查詢出來的是笛卡爾積結果(M*N)
通常會對不一樣表起別名來便於寫指令
SQL通常寫成多行以便於閱讀:
最規範的寫法
另外一種寫法:
如何寫子查詢?能夠先分開寫多條(如上),而後合爲一條
查詢結果爲多行多列的狀況,實際上就是將第一次查詢出來的結果做爲一張暫存的表再和其餘表進行組合等操做
控制檯cmd默認操做是gbj,數據是utf8,中文字符可能會有顯示問題
事務不提交以前是不會對數據庫數據出現修改的,若是關閉窗口會自動回覆事務前的狀況
MySQL中事務默認自動提交,選擇START TRANSACTION以後切換到手動提交模式,COMMIT命令以前是不會提交的。固然,咱們也能夠查看和修改事務的默認提交方法(1:自動提交,0:手動提交)注意,Oracle是手動提交的
對於MySQL,使用手動提交事務正確的操做方式爲:
多個事務之間,咱們想要努力作到隔離,雖然經常不能徹底隔離
事務的一致性是創建在事務每每的應用背景下的
注意,幻讀這裏的描述不許確,不是「查詢不到本身的修改」,而能夠描述爲「發現本身的修改沒有應用到全部記錄上(也就是說有例外)"
MySQL默認隔離級別爲repeatable read,而Oracle是read committed
查詢和設置隔離級別:
Java數據庫鏈接,(Java Database Connectivity,簡稱JDBC)是Java語言中用來規範客戶端程序如何來訪問數據庫的應用程序接口,提供了諸如查詢和更新數據庫中數據的方法。 JDBC也是Sun Microsystems的商標。 JDBC是面向關係型數據庫的。
Java定義了一套操做全部關係型數據庫的規則(接口),具體的實現由各自的數據庫軟件負責,從而實現用一套統一的Java代碼操做全部的關係型數據庫,即以接口類型調用方法,真正實現的是實現類中的方法
JDBC快速入門:
package com.jiading.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; //JDBC入門 public class JDBCDemo1 { public static void main(String[] args) throws Exception { //導入驅動jar包 //註冊驅動 Class.forName("com.mysql.cj.jdbc.Driver"); //獲取數據庫鏈接對象 Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/jd1?serverTimezone=UTC","root","root"); //定義SQL語句 String sql="update test set figure1 =10 where figure2=3"; Statement stmt=conn.createStatement(); //執行SQL int count=stmt.executeUpdate(sql); System.out.println(count); stmt.close(); conn.close(); } }
Class.forName()將類的字節碼文件加載到內存中
這裏注意註冊驅動部分的路徑怎麼寫,首先須要先導入jar包,從網絡中下載mysql-connect的jar包,將其導入IDEA。導入的過程以下:
先說下第一種方法吧。也就是
File –> Project Structure導入方法
先是進入:File –> Project Structure
(Error:This picture is not found in the List!)>
再找到Modules->Dependencies
點擊最右側的綠色+號
如圖:(Error:This picture is not found in the List!)>
選擇1或者2都行的:(Error:This picture is not found in the List!)>
而後就是選擇你要導入的Jar包了。而後再講下第二種方式。
右鍵添加Jar包
也就是在你須要導入的Jar包上,點擊右鍵,選擇Add as Library…
(Error:This picture is not found in the List!)>
點擊OK就好了。(Error:This picture is not found in the List!)
以後按着jar包的目錄輸入:
題外話,URL中,?被用來分割URL自己和參數,例如這裏URL中加入的serverTimezone=UTC就是由於鏈接時出現了異常「The server time zone value '?й???????' is unrecognized or represents more than one time zo」
在使用Class.forName()將該Driver類加載到內存時,靜態代碼塊被執行,調用DriverManager的靜態方法registerDriver進行了驅動的註冊,因此以後咱們才能直接調用DriverManager的靜態方法使用。
省略註冊驅動指的是Class.forName能夠省略了:驅動jar包在沒有註冊驅動時,自動讀取META-IN\services下的java.sql.Driver文件進行註冊
MySQL默認的端口號是3306
Statement執行的靜態SQL,PreparedStatement能夠執行動態SQL
不常常用statement執行DDL:由於Connection是創建在數據庫上的(即URL中有具體的數據庫),因此不能用這個命令來處理數據庫,而即便是表的設計也是一個很負責的過程,通常都是用SQL的軟件完成的,不多用代碼直接完成。
剛纔代碼存在的問題:
示例代碼以下:
package com.jiading.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; //JDBC入門 public class JDBCDemo2 { public static void main(String[] args) { //導入驅動jar包 //註冊驅動 Statement stmt=null; Connection conn=null; try { Class.forName("com.mysql.cj.jdbc.Driver"); String sql="update test set figure1 =10 where figure2=3"; try { conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/jd1?serverTimezone=UTC","root","root"); stmt=conn.createStatement(); int count=stmt.executeUpdate(sql); System.out.println(count); if(count>0){ System.out.println("添加成功!"); } else{ System.out.println("添加失敗"); } } catch (SQLException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ /* 先釋放stmt,再釋放conn,由於stmt是由conn建立的 爲了可以在finally中釋放這兩個變量,須要將它們定義在try以前,先賦值爲Null(這個術語叫變量的抽取) 一樣的,爲了不空指針異常,咱們須要在釋放前先判斷,若是是null就不釋放了 */ if(stmt!=null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn!=null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } //獲取數據庫鏈接對象 //定義SQL語句 //執行SQL } }
注意,這裏的註釋有錯位
executeupdate方法不只能夠執行DML,還能夠執行DDL語句,對錶進行修改
其實就是對查詢的結果的封裝。
ResultSet遊標:查詢開始時指向表第一行的上一行
next():遊標向下一行。注意,這裏移動遊標獲取以前必定要先加一個判斷,判斷是否有數據。這個判斷不須要咱們去寫,而是next方法本身就會返回的:next方法是一個boolean類型的方法,若是調用了以後當前行沒有數據其會返回null,不然會返回true。以此爲條件就能判斷了。將next方法寫在while中,還能夠實現循環遍歷
getXXX(參數):XXX表明數據類型,這是獲取數據的方法,例如getInt\getString等。注意,獲取數據時不是把一行的全部數據都獲取了,而是隻獲取某一列的數據
其中參數能夠接受Int或者String。傳入Int表明獲取該行中第幾列的值(注意,這裏特殊的是,標號是從1開始的),傳入String則是具體的指明獲取的列的label.
定義一個方法,查詢emp表的數據將其封裝爲對象,而後裝載集合,返回
代碼以下:
package com.jiading.jdbc; /* 封裝emo表數據的JavaBean */ public class Jd1{ private int day; private int year; public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } @Override public String toString() { return "JDBCDemo3{" + "year=" + year + ", day=" + day + '}'; } }
package com.jiading.jdbc; import java.sql.*; import java.util.ArrayList; import java.util.List; public class JDBCDemo3 { public static void main(String[] args) { //這裏又用到了匿名內部類,而且這裏的類仍是咱們寫的這個java類 List<Jd1>e=new JDBCDemo3().findall(); System.out.println(e); } public List<Jd1> findall(){ /* 查詢全部Jd22對象 */ /* 若是將對象的建立寫在循環內,會產生許多引用。 此時的一個優化策略是將引用寫在外面,先賦值爲null,在循環中每次new新對象便可 */ Jd1 re=null; List<Jd1>list=null; Connection conn=null; Statement stmt=null; ResultSet rs=null; try { Class.forName("com.mysql.jdbc.Driver"); conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/jd1?serverTimezone=UTC","root","root"); String sql="select * from test"; stmt=conn.createStatement(); rs=stmt.executeQuery(sql); /* 這裏注意,List在Java中是一個抽象類,是不能直接實例化的,而ArrayList則是對List的一種實現,實際實例化的經常是ArrayList */ list=new ArrayList<Jd1>(); while(rs.next()){ int year=rs.getInt("year"); int day=rs.getInt("day"); re=new Jd1();//引用綁定到新對象上 re.setDay(day); re.setYear(year); list.add(re); } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); }finally{if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stmt!=null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } return list; } }
Sql注入問題:
如圖,and和or都是左結合的,最後and是嵌套在or裏面的
解決:使用PreparedStament對象
PreparedStament是Statement的子接口,可是其是預編譯的SQL:參數使用佔位符替代,例如:
select * from user where username =? and password=?;
獲取執行sql語句的對象 PreparedStatemnet connnection.preparedStatment(string sql);//和獲取statement不一樣,獲取PreparedStatement對象是要傳參數的
因爲咱們的sql中內容是以佔位符形式代替的,因此將sql傳入後須要先給它賦值(先傳再賦值)
實際狀況下使用的大多都是PreparedStatement來完成增刪查改,它不只能夠防止SQL注入,並且效率更高
這個是本身搭建的,用於簡化重複的代碼
其實Connection也是工具類,工具類有個特色,就是全部的方法都是靜態的,以便咱們進行調用具體的方法解決問題,而不至於一直關注於對象的建立(例如String類的valueof方法就是靜態的,目的是爲了其餘類型向String的轉化)。因此,咱們在寫的時候也這樣作。
步驟:
注意,不是每一次使用咱們都須要註冊驅動的,因此註冊驅動的部分咱們寫成一個單獨的方法,須要的時候再調用
事務的管理是由Connection對象完成的
這裏catch的是一個大的異常,以便於在遇到各類問題的時候都回滾。固然,咱們須要先判斷一下conn是否是null,由於可能在conn實例化以前就發生了異常,此時就不須要回滾了。
鏈接對象用完了不釋放,而是放入鏈接池,以後須要直接調用
接口由Sun公司定義,由數據庫方實現
實現:
對於用戶來講,歸還和以前使用的銷燬對象是沒有區別的
這個東西通常咱們不去實現,而是由數據庫廠商實現
今天講解兩種不一樣的實現形式:
使用起來有兩種方法:硬編碼和配置文件,通常推薦使用配置文件方法,以加強通用性
注意開發環境,jar包的版本
c3p0-config.xml必須放在src根目錄下
配置文件參數講解:
最大鏈接數:同時能使用的最大鏈接數量
<default-config>和<named-config name="otherc3p0">的區別: c3p0容許用戶在配置文件中保存多種配置,使用name進行區分,在new ComboPolledDataSource()時若是括號什麼都不寫,就使用默認配置;要是括號寫了name的字符串,就使用對應的配置
注意,druid和C3P0相比:
有些框架不須要獲取鏈接池對象,只須要鏈接池便可,因此要加上獲取鏈接池的方法
Spring JDBC
使用Junit單元測試:可讓方法獨立執行,而再也不依賴於主方法。須要在以前加一個@Test.最重要的是,這樣咱們就能夠建立多個方法,分別對其進行調試,而相互之間不會影響,也不須要主方法,很是利於調試
能夠將獲取JdbcTemplate的部分做爲成員變量,這樣就不須要每一個方法都獲取一次了。
注意,queryForMap()查詢的結果集長度只能是1:若是本次查詢會返回兩個及以上結果,則會報錯。因此若是結果有多是多個,應該使用queryForList()或者query()
queryForList()是將每一條記錄封裝爲一個map集合,再將map裝載到List中
若是要將查詢結果封裝爲其餘類型,就使用query()方法,在傳入sql的同時再傳入RowMapper接口類型的對象,這裏咱們本身本身實現一下:
最重要的是要實現一下RowMapper的mapRow方法
注意,咱們在本身實現的時候就能夠填要返回的類型了,這裏填的是需求中的Emp類(泛型)。
而RowMapper也本身提供了一些實現類,有的是很經常使用的,不須要每次都本身去實現。
這裏的泛型填本身要返回的類型就行了,固然,咱們的類的屬性要和所查詢的屬性名稱以及類型相一致。
這裏有一個常見的問題:咱們在定義類的時候若是使用的是基本數據類型,那麼它是不能接受Null的,這樣在sql中就可能會有問題:由於有些數據庫中的數據返回的就是null,因此咱們應該使用引用數據類型,例如Int、Double等。
對於queryForObject()方法,咱們初始化時要輸入返回的結果的類型,例如這裏的Long.class,返回的就是Long這個類。