這個屬性的name必需要和類中File名稱同樣
第三步:在Action類中添加如下屬性
public class HelloWorldAction{
private File uploadImage;//獲得上傳文件;
private String uploadImageContentType;//獲得文件的類型
private String uploadImageFileName;//獲得文件的名稱
//這裏省略了屬性的get/set方法(可是要注意get/set方法是必須的)
public String upload() throws Exception{
String realpath =ServletActionContext.getServletContext().getRealPath("/images");
File file=new File(realpath);
if(file.getParentFile().exists())file.getParentFile().mkdirs();//目錄是否存在,不存在就建立
FileUtils.copyFile(uploadImage,newFile(file,uploadImageFileName));
return "success";
}
}
(1).若是文件不保存,struts2會把文件保存到本身的目錄中,可是當這個Action執行完後,該文件就會被刪除,因此咱們要將上傳的文件保存到硬盤上
(2).最好還要判斷如下,文件uploadImage是否爲空
(3).若是上傳大的文件,web都會失敗,像一些門戶網站上傳視頻,都是經過一個插件,能夠把這個插件當作一個程序,只是這個程序是經過Socket變成的,針對一個端口進行傳輸數據
27.指定struts2處理的請求後綴
(1).前面咱們都是默認使用.action後綴訪問Action,其實默認後綴是能夠經過常量"struts.action.extension"進行修改的,例如:咱們能夠配置struts2只處理以.do爲後綴的請求路徑
<constantname="struts.action.extendsion" value="do"/>
若是用戶須要制定多個請求後綴,則多個後綴之間以英文逗號","隔開,如:
<constantname="struts.action.extendsion" value="do,go"/>
(2).常量能夠在struts.xml或struts.properties中配置,建議在struts.xml中配置,兩種配置方式以下:
在struts.xml文件中配置常量:
<constantname="struts.action.extendsion" value="do"/>
在struts.properties中配置常量:
struts.action.extension=do
由於常量能夠在下面多個配置文件中進行定義,因此咱們須要瞭解struts2加載常量的搜索順序:
struts-default.xml
struts-plugin.xml
struts.xml
struts.properties
web.xml
若是在多個文件中配置了同一個常量,則後一個文件中配置的常量值會覆蓋前面文件中配置的常量值
(3).
第一:默認編碼集,做用於HttpServletRequest的setCharacterEncoding方法和freemarker、velocity的輸出:
<constantname="struts.i18n.encoding" value="UTF-8"/>
第二:該屬性指定須要struts2處理的請求後綴,該屬性的默認值是action,即全部匹配*.action的請求都由struts2處理,若是用戶須要指定多個請求後綴,則多個後綴之間以英文逗號(,)隔開
第三:設置瀏覽器是否緩存靜態內容默認值爲true(生產環境下使用)開發階段最好關閉,否則看不到修改後的數據
<constantname="struts.serve.static.browserCache" value="false"/>
第四:當struts的配置文件修改後系統是否自動從新加載該文件默認值爲false(生產環境下使用),開發階段最好打開
<constantname="struts.configuration.xml.reload" value="true"/>
第五:開發模式下使用,這樣能夠打印出更詳細的錯誤信息
第六:默認的視圖主題
<constantname="struts.ui.theme" value="simple"/>
第七:與spring集成時,指定由spring負責action對象的建立
<constantname="struts.objectFactory" value="spring"/>
第七:該屬性設置struts2是否支持動態方法調用,該屬性的默認值是true,若是須要關閉動態方法調用,則可設置該屬性爲false
<constantname="struts.enable.DynamicMethodInvocation"value="false"/>
第八:上傳全部文件的總大小限制
constantname="struts.mulitipart.maxSize" value="10701096"/>
28.自定義攔截器
(1).若是用戶登陸後能夠訪問action中的全部方法,若是用戶沒有登陸不容許訪問action中的方法,而且提示"你沒有權限執行該操做"
(2).
<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
<actionname="list_*" class="cn.itcast.action.HelloWorldAction"method="{1}">
<interceptor-refname="permission"/>
若是爲某一個Action定義一個攔截器,struts2中對Action的默認的不少攔截器都失去功能,因此要想作到一箭雙鵰,須要定義一個攔截器棧:
<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
<interceptor-stackname="permissionStack">
<interceptor-refname="defaultStack"/>
<interceptor-refname="permission"/>
由於struts2中如文件上傳,數據驗證,封裝請求參數到action等功能都是由系統默認的defaultStack中的攔截器實現的,因此我 們定義的攔截器須要引用系統默認的defaultStack,這樣應用才能夠使用struts2框架提供的衆多功能,若是但願包下的全部action都使 用自定義的攔截器,能夠經過<default-interceptor-refname="permissionStack"/>把攔截器定 義爲默認攔截器,注意:每一個包只能指定一個默認攔截器,另外,一旦咱們爲該包中的某個action顯示指定了某個攔截器,則默認攔截器不會起做用.
(3).系統默認的攔截器能夠到struts-default.xml中查看,不少功能.系統攔截器放在最前面,自定義的攔截器放在後面.
29.自定義類型轉換器
(1).struts2中提供了兩種類型轉換器:局部類型轉換器(只對某一個action起做用),全局類型轉換器(全部的action起做用)
(2).類型轉換器必須繼承DefaultTypeConverter最好用xwork2.jar中的,重寫converValue(Map<String,Object>context,Objectvalue,Class toType){
returnsuper.convertValue(context,value,toType);
}
其中第一個參數和ognl表達式,第二個參數是須要轉換類型的內容(是String數組,由於可能有多個值),第三個參數是須要轉換成什麼類型,要實現雙向轉換
(3).將上面的類型轉換器註冊爲局部類型轉換器:
在Action類所在的包下放置ActionClassName-conversion.properties文 件,ActionClassName是Action的類名,後面的-conversion.properties是固定寫法,對於本例而言,文件的名稱應 爲HelloWorldAction-conversion.properties.在properties文件中的內容爲:
須要轉換的屬性名稱=類型轉換器的全類名
對於本例而言,HelloWorldAction-conversion.properties文件中的內容爲:
createtime=cn.itcast.conversion.DateConverter
4、 Spring
@Autowired註解與自動裝配
@Autowired
private PersonDaopersonDao;
拿PersonDao與<bean id=""..../>中的id值進行比較,相同就找到了,即進行類型註解,固然也能夠過@Qualifier註解進行名稱進行註解.
自動裝配:
對於自動裝配,你們瞭解一下就能夠了,實在不推薦你們使用,例子:
autowire屬性取值以下:
byType:按類型裝配,能夠根據屬性的類型,在容器中尋找根該類型匹配的bean,若是發現多個,那麼將會拋出異常,若是沒有找到,即屬性值爲null
byName:按名稱裝配,能夠根據屬性的名稱,在容器中尋找跟該屬性名相同的bean,若是沒有找到,即屬性值爲null
construtor與byType的方式相似,不一樣之處在於它應用於構造器參數,若是在容器中沒有找到與構造器參數類型一致的bean,那麼將會拋出異常.
autodetect:經過bean類的自省機制,來決定是使用constructor仍是byType方式進行自動裝配,若是發現默認的構造器,那麼將使用byType方式.
@Resource註解完成屬性裝配
(1).前面講到了使用構造器注入,屬性的setter方法注入,這裏還能夠使用註解的方式對Field進行注入
(2).注入依賴對象能夠採用手工裝配或自動裝配,在實際應用中建議使用手工裝配,由於自動轉配會產生未知狀況,開發人員沒法預見最終的裝配結果
(3).手工裝配依賴對象,在這種方式中又有兩種編程方式
方式一:在XML配置文件中,經過在Bean節點下配置,如:
構造器注入
屬性的setter方法注入
在XML中注入屬性,會給XML文件變得很臃腫.特別是對集合類型進行注入時,變得很臃腫.
方式二:
在java代碼中使用@Autowire或@Resoruce註解方式進行裝配,但咱們須要在XML配置文件中配置如下信息:
<beansxmlns="http://www.springframe.....
....
....
這些配置項隱式註冊了多個對註釋進行解析處理的處理 器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequireAnnotationBeanPostProcessor, 每一個註解都有一個註解處理器,註解自己不幹活,是相對應的註解處理器在幹活
注:@Resource註解在spring安裝目錄的lib\j2ee\common-annotations.jar
(4).在java代碼中使用@Autowired或@Resource註解方式進行裝配,這兩個註解的區別是:
@Autowired默認按類型裝配,@Resource默認按名稱裝配,當找不到與名稱匹配的bean纔會按類型裝配.
@Autowired
private PersonDaopersonDao;//用於字段上
@Autowired
public voidsetOrderDao(OrderDao orderDao){//用於setter方法上
this.orderDao=orerDao;
}
@Autowired註解是按類型裝配依賴對象,默認狀況下,它要求依賴對象必須存在,若是容許null值,能夠設置它required的屬性爲false,若是咱們想使用按名稱裝配,能夠結合@Qualifier註解一塊兒使用,以下:
@Autowired@Qualifier("personDaoBean")
private PersonDaopersonDao;
@Resource註解和@Autowired同樣,也能夠標註在字段或屬性的setter方法上,但他默認按名稱裝配,名稱能夠經過 @Resource的name屬性執行,若是沒有指定name屬性,當註解標註在字段上,即默認取字段的名稱做爲bean名稱尋找依賴對象,當註解標註在 屬性的setter方法上,即默認取屬性名做爲bean名稱尋找依賴對象
@Resource(name="personDaoBean")
private PersonDaopersonDao;//用於字段上
注意:若是沒有指定name屬性,而且按照默認的名稱仍然找不到依賴對象時,@Resoruce註解會回退到按類型裝配,但一旦指定了name屬 性,就只能按名稱裝配了.拿personDao與<bean id=" ".../>中的id是否相同,相同就找到,屬性的setter方法也不用寫,既方便,又優雅.
同時@Resource是j2ee提供的註解(建議使用),@Autowired是spring框架提供的.
Spring的三種實例化Bean的方式
(1).三種實例化bean的方式:
第一種:<bean id="orderService"class="cn.itcast.OrderServiceBean/>
第二種:使用靜態工廠方法實例化:
public class OrderFactory{
public static OrderServiceBeancreateOrder(){
return new OrderServiceBean();
}
}
第三種:使用實例工廠方法實例化:
<beanid="personServiceFactory" class="cn.itcast.service.OrderFactory"/>
public class OrderFactory{
public OrderServiceBean createOrder(){
return new OrderServiceBean();
}
}
Spring管理的Bean的生命週期
(1).Bean實例化是在Spring容器實例化時進行的,可是這是Singleton做用域中,實例化的時機是能夠更改的,lazy- init="true"延遲初始化,即更改成調用getBean()方法時進行初始化.同時也能夠在配置文件中設置全部的bean延遲初始化,其實這個標 籤是不建議使用的.
(2).當把做用域改爲Prototype時,Bean實例化是在調用getBean()方法進行的
(3).能夠指定一個初始化方法:init-method="";即在bean實例化後執行的初始化方法.如數據庫的鏈接.容器經過反射技術調用的,同時還須要進行資源的釋放.destory-method="";即在bean被銷燬時執行的方法.
(4).關閉Spring容器:ctx.close()方法,bean此時被銷燬了
5.Spring自動掃描和管理bean
經過在classpath自動掃描方式把組件歸入spring容器中管理,前面的例子咱們都是使用XML的bean定義來配置組件,在一個稍大的項 目中,一般會有上百個組件,若是這些組件採用xml的bean定義來配置,顯然會增長配置文件的體積,查找及維護起來也不太方便,spring2.5爲我 們引入了組件自動掃描機制,它能夠在類路徑底下尋找標註了@Componet、@Service、@Controller、@Reponsitory註解 的類,並把這些類歸入進spring容器中管理,它的做用和在XML文件中使用bean節點配置組件是同樣的,要使用自動掃描機制,咱們須要打開如下配置 信息:
<beansxmln="http://www.springframework.org/schema/beans"
....
....
其中base-package爲須要掃描的包(含子包)
@Service用於標註業務層組件、@Controller用於標註控制層組件(如struts中的action)、@Repository用於 標註數據訪問組件,即Dao組件,而@Component泛指組件,當組件很差歸類的時候,咱們能夠使用這個註解進行標註.同時也能夠經過註解 @Scope("prototype")修改bean的做用域.@Service("personService")中的名稱必須和bean的名稱相同, 只是開頭字母變成小寫了.固然這是默認設置,能夠修改的.
能夠使用註解的方式指定初始化方法,在初始化方法init()上添加註解@PostConstruct,一樣能夠指定銷燬方法destroy(),註解爲:@PreDestroy
這種掃描方式是很方便的,不少人都採用
即前面所說的功能都使用註解進行操做
6. SSH整合開發
hibernate核心安裝包下的:
hibernate3.jar
lib\required*.jar
lib\optional\ehcache-1.2.3.jar
hibernate註解安裝包下的
lib\test\slf4j-log4j12.jar
Spring安裝包下的
dist\spring.jar
dist\modules\spring-webmvc-struts.jar
lib\jakarta-commons\commons-logging.jar、commons-dbcp.jar、commons-pool.jar
lib\aspectj\aspectjweaver.jar、aspectjrt.jar
lib\cglib\cglib-nodep-2.1.3.jar
lib\j2ee\common-annotations.jar
lib\log4j-1.2.14.jar
Struts:下載struts-1.3.8-lib.zip,須要使用到解壓目錄下的全部jar,建議把jstl-1.0.2.jar和 standard-1.0.2.jar更換爲1.1版本,Spring中已經存在一個antlr-2.7.6.jar,因此把struts中的 antlr-2.7.2.jar刪除,避免jar衝突
數據庫驅動jar
首先整合struts和hibernate而後再整合spring
7.編碼解析@Resource註解的實現原理
(1).新建一個註解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.Field,ElementType.METHOD})
public @interface ItcastResource{
String name() default "";
}
固然還要編寫一個註解處理器.
private voidannotationInject(){
首先循環全部的bean對象,判斷其是否使用了註解
若是使用了註解,就進行屬性注入.
}
8.編碼解析Spring依賴注入的原理
(1).基本類型對象注入:
構造器注入
//屬性setter方法注入
(2).注入其餘bean:
方式一:
ref是指被注入的對象personDao
方式二:(使用內部bean,但該bean不能被其餘bean使用)
<beanclass="cn.itcast.service.OrderDaoBean"/>
(3).依賴注入的內部原理
9.編碼解析Spring裝配基本屬性原理
也能夠對基本類型進行注入,類型轉換.
10.編碼剖析Spring管理bean的原理
使用dom4j讀取spring配置文件,使用反射技術便可
11.搭建和配置Spring與JDBC整合的環境
(1).配置數據源:
apache的數據源:BasicDataSource
<propertyname="driverClassName" value="org.gjt.mm.mysql.Driver"/>
鏈接池啓動時的初始值
鏈接池的最大值
最大空閒值
最小空閒值
配置事務:
採用註解的方式:
<ts:annotation-driventransaction-manager="txManage"/>
12.搭建與測試spring的開發環境
(1).dist\spring.jar;
lib\jakarta-commons\commons-logging.jar
以上這兩個包是必須的
lib\aspectj\aspectjweaver.jar和aspectjrt.jar
lib\cglib\cglib-nodep-2.1.3.jar
以上這兩個包是用於切面編程(AOP)
lib\j2ee\common-annotations.jar
以上的這個包是JSR-250中的註解
(2).Spring項目既能夠在j2se中也能夠在j2ee中
(3).spring的配置文件模板能夠從spring的參考手冊或spring的例子中獲得,配置文件的取名能夠任意,文件能夠存放在任何目錄下,但考慮到通用性,通常放在類路徑下
(4).實例化spring容器經常使用方式:
第一種方式:在類路徑下尋找配置文件來實例化容器
ApplicationContextctx=new ClassPathXmlApplicationContext(new String[]{"beans.xml"});
第二種方式:在文件系統路徑下尋找配置文件來實例化容器
ApplicationContextctx=new FileSystemXmlApplicationContext(newString[]{"d:\beans.xml"});//將路徑寫死了,通用性很差,不建議使用.
spring的配置文件能夠指定多個,能夠經過String數組傳入
(5).IOC:控制反轉:
public class PersonServiceBean{
private PersonDao personDao = newPersonDaoBean();
public void save(Person person){
personDao.save(person);
}
}
PersonDaoBean是在應用內部建立及維護的,所謂控制反轉就是應用自己不負責依賴對象的建立及維護,依賴對象的建立及維護是由外部容器負責的,這樣控制權就由應用轉移到外部容器,控制權的轉移就是所謂反轉.
(6).創建一個業務bean爲PersonServiceBean,放在cn.itcast.service.impl包中.面向接口編程,進行 解耦,怎麼將業務bean交給spring管理,須要在beans.xml配置文件中:
,其中id,name都是bean的名稱,可是id不能使用特殊字符,id自己就屬於 XML屬性的,如:"/sfs/"就會出錯,可是name不會出錯,class屬性是bean類的路徑,須要操縱bean的時候,只需到spring容器 中取出bean進行操做,而不須要實例化一個bean了:
ApplicationContext ctx=newClassPathXmlApplicationContext(new String[]{"beans.xml"});
PersonServiceBeanpersonService=(PersonServiceBean)ctx.getBean("personService");
personService.save();
(7).當在配置文件中沒有標籤的提示信息,須要手動添加schema文件,方法如 下:windows->preferences->myeclipse->filesand editors->xml->xmlcatalog,點擊添加,在出現的窗口中的Key Type中選擇URL,在location中選"File System",而後在spring解壓目錄的dist/resources目錄中選擇spring-beans-2.5.xsd,回到設置窗口的時候不 要急着關閉窗口,應該把窗口的Key Type改成Schema location,Key改成http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
13.配置Spring管理的bean的做用域
(1).Singleton:默認狀況下,bean是單例模式的,在每一個springIoc容器中一個bean定義只有一個對象實例,默認狀況下會 在容器啓動時初始化bean,可是咱們能夠指定Bean節點的lazy-init="true"來延遲初始化bean,這時候,只有第一次獲取bean會 才初始化bean,如:
若是想對全部bean都應用延遲初始化,能夠在根節點beans設置default-lazy-init="true",以下:
Prototype:每次從容器獲取bean都是新的對象
Request:在request的域中
Session:在Session的域中
Global Session:在全局的Session的域中
14.全面闡釋Spring及其各項功能
(1).Spring是一個開源的控制反轉(Inversion of control,IoC)和麪向切面(AOP)的容器框架,它的主要目的是簡化企業開發
(2).IOC:控制反轉:
public class PersonServiceBean{
private PersonDao personDao = newPersonDaoBean();
public void save(Person person){
personDao.save(person);
}
}
PersonDaoBean是在應用內部建立及維護的,所謂控制反轉就是應用自己不負責依賴對象的建立及維護,依賴對象的建立及維護是由外部容器負 責的,這樣控制權就由應用轉移到外部容器,控制權的轉移就是所謂反轉.PersonServiceBean是業務邏輯層類,PersonDaoBean是 Dao層類,在業務邏輯層類中控制和建立PersonDaoBean,這就是應用自己建立和維護了,可是有了spring容器 後,PersonDaoBean的建立和維護是在容器中進行的,不須要PersonServiceBean進行管理了,控制權進行的反轉
(3).依賴注入:當咱們把依賴對象交給外部容器負責建立,那麼PersonServiceBean類能夠改爲以下:
public class PersonServiceBean{
private PersonDao personDao;
//經過構造器參數,讓容器把建立好的依賴對象注入進PersonServiceBean,固然也能夠使用setter方法進行注入
public PersonServiceBean(PersonDaopersonDao){
this.personDao=personDao;
}
public void save(Person person){
personDao.save(person);
}
}
所謂依賴注入就是指:在運行期,由外部容器動態的將依賴對象注入到組件中.
(4).爲什麼要使用spring:
第一:下降組件之間的耦合度,實現軟件各層之間的解耦:
Controller->Service->Dao
第二:能夠使用容器提供的衆多服務,如:事務管理服務,消息服務等,當咱們使用容器管理事務時,開發人員就再也不須要手工控制事務,也不須要處理複雜的事務傳播
第三:容器提供單例模式支持,開發人員不在須要本身編寫實現代碼
第四:容器提供了AOP技術,利用它很容易實現如權限攔截、運行期監控等功能
第五:容器提供的衆多輔助類,使用這些類可以加快應用的開發,如:JdbcTemplate,HibernateTemplate
第六:Spring對於主流的應用框架提供了集成技術,如集成Hibernate、JPA、Struts等,這樣更便於應用的開發.
(5).輕量級和重量級概念的劃分,其實劃分一個應用是否屬於輕量級仍是重量級,主要看他使用了多少服務,使用的服務越多,容器要爲普通的java 對象的工做就越多,必然會影響到應用的發佈時間或者是運行性能,對於spring容器,它提供了不少服務,可是這些服務並非默認爲應用打開的,應用須要 某種服務,還須要指明使用該服務,若是應用使用的服務不多,如:只是用了spring的核心服務,那麼咱們就能夠認爲此時應用屬於輕量級的,若是應用使用 了spring提供的大部分服務,這時應用就屬於重量級,目前EJB容器就由於他默認爲應用提供了EJB規範中全部的功能,因此他屬於重量級
15.使用CGLIB實現AOP功能與AOP概念詳解
(1).使用cglib架包,構建代理,不須要被代理的對象須要實現接口
public class CGlibProxyFactory implementsMethodInterceptor{
private Object targetObject;
public Object createProxyIntance(ObjecttargetObject){
this.targetObject=targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercep(Object proxy,Method method,Object[]args,MethodProxy methodProxy)throws Throwable{
returnmethodProxy.invoke(this.targetObject,args);
}
}
CGLIB能夠生成目標類的子類,並重寫父類非final修飾符的方法
16.使用JDK中的Proxy技術實現AOP功能
(1).使用在權限攔截上.
(2).被代理對象必須實現接口
public class JDKProxyFactory implementsInvocationHandler{
private Object targetObject;
public Object createProxyIntance(ObjecttargetObject){
returnProxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy,Methodmethod,Object[]args) throws Throwable{//環繞通知
PersonServiceBean bean=(PersonServiceBean)this.targetObject;
Object result=null;
if(bean.getUser()!=null){
//....advice(),調用方法前處理,叫作前置通知
try{
result=method.invoke(targetObject,args);
//....afteradvice();調用方法後處理,叫作後置通知
}catch(RuntimeException e){
//....exceptionadvice();調用方法出現例外後處理,叫作例外通知
}finally{
//....finallyadvice();調用方法最終都會處理,叫作最終通知
}
}
return result;
}
}
整個invoke方法叫作環繞通知.
(3).AOP中的概念:
Aspect(切面):指橫切性關注點的抽象即爲切面,它與類類似,只是二者的關注點不同,類是對物體特徵的抽象,而切面是橫切性關注點的抽象。
JoinPoint(鏈接點):所謂鏈接點是指那些被攔截到的點,在spring中,這些點指的是方法,由於spring只支持方法類型的鏈接點,實際上joinpoint還能夠是field或類構造器
Pointcut:切入點:所謂切入點是指咱們要對那些joinpoint進行攔截的定義
Advice(通知):所謂通知是指攔截到joinpoint以後所要作的事情就是通知,通知分爲前置通知,後置通知,異常通知,最終通知,環繞通知
Target(目標對象):代理的目標對象
Weave(織入):指將aspect應用到target對象並致使proxy對象建立的過程稱之爲織入
Introduction(引入):在不修改類代碼的前提下,Introduction能夠在運行期爲類動態的添加一些方法或Field
17.使用Spring的註解方式實現AOP
(1).要進行AOP編程,首先咱們要在Spring的配置文件中引入AOP命名空間:
<beansxmlns="http://www.springframework.org/schema/beans"
Spring提供了兩種切面使用方式,實際工做中咱們能夠選用其中一種:一種是基於XML配置方式進行AOP開發,另一種是基於註解方式進行AOP開發
(2).基於註解方式聲明切面:首先啓動對@AspectJ註解的支持:
定義一個切面類(MyInterceptor):
@Aspect
public class MyInterceptor{
@Pointcut("execution(cn.itcast.service...*(..))
//定義切入點,通配符:指的是任何返回類型,..是指子包下也進行攔截,:指的是攔截全部類,*:指定的是全部方法,..是指任意參數
private void anyMethod(){}//聲明一個切入點
@Before("anyMethod()")//定義一個前置通知,名稱爲切入點名稱
public void doAccessCheck(){
System.out.println("前置通知");
}
@AfterReturning("anyMethod()")//後置通知
public void doAfterReturning(){
System.out.println("後置通知");
}
@After("anyMethod()")//最終通知
public void doAfter(){
System.out.println("最終通知");
}
@AfterThrowing("anyMethod()");//例外通知
public void doAfterThrowing(){
System.out.println("例外通知");
}
@Around("anyMethod()")//環繞通知
public ObjectdoBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
System.out.println("進入方法");
if(){//判斷用戶是否有權限,有權限就執行該方法.
Object result = pjp.proceed();
System.out.println("退出方法");
}else{
}
return result;
}
}
因此當出現例外通知時,後置通知是不執行的,即它們二者確定有一個不執行,一個執行.
須要將切面類交給spring管理:基於XML配置管理或者自動掃描管理
(3).想獲得參數:
@Before(anyMethod() &&args(userName)")//添加參數,只會攔截到對應的方法,即一個參數的方法.
public void doAccessCheck(String name){
System.out.println("前置通知");
}
(4).想獲得返回結果:
@AfterReturning("anyMethod()",returning="result")//後置通知
public void doAfterReturning(String result){
System.out.println("後置通知");
System.out.println(result);//打印返回結果
}
(5).獲得異常(例外):
@AfterThrowing("anyMethod()",throwing="e");//例外通知
public void doAfterThrowing(Exception e){
System.out.println("例外通知");
System.out.println(e);//打印例外
}
18.使用Spring配置文件實現AOP
(1).使用配置文件實現AOP,切面類只是個普通的類,其內部沒有任何註解
public class MyInteceptor{
public void doAccessCheck(){
System.out.println("前置通知");
}
public void doAfterReturning(){
System.out.println("後置通知");
}
public void doAfter(){
System.out.println("最終通知");
}
public void doAfterThrowing(){
System.out.println("例外通知");
}
public ObjectdoBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
System.out.println("進入方法");
Object result=pjp.proceed();
System.out.println("退出方法");
return result;
}
}
(2).基於XML配置方式聲明切面
<aop:beforepointcut-ref="mycut" method="doAccessCheck"/>
<aop:after-returningpointcut-ref="mycut" method="doReturnCheck"/>
<aop:after-throwingpointcut-ref="mycut" method="doExceptionAction"/>
<aop:afterpointcut-ref="mycut" method="doReleaseAction"/>
<aop:aroundpointcut-ref="mycut" method="doBasicProfiling"/>
(3).對於表達式expression的細節:
返回值類型爲String:execution(java.lang.Stringcn.itcast.service...(..))
第一個參數爲String:execution(java.lang.Stringcn.itcast.service...(java.lang.String..))
返回值類型爲非void:execution(!void cn.itcast.service...(..))
19.使用Spring配置文件實現事務管理
(1).
<aop:pointcutid="transactionPointcut" expression="execution(cn.itcast.service..*(..))"/>對指定的方法進行攔截
<aop:advisoradvice-ref="txAdvice"pointcut-ref="transactionPointcut"/>
<tx:adviceid="txAdvice" transaction-manager="txManager">
<tx:methodname="*"/>
20.使用Spring註解方式管理事務與傳播行爲詳解
(1).只有當遇到RuntimeException時,事務進行回滾.Spring開啓的事務管理,當遇到運行期例外(unchecked),而(checked異常)是不進行事務的回滾的.
(2).固然也可修改這種狀況.把unckecked異常改爲不會進行回滾了:@Transactional(noRollbackFor=RuntimeException.class),
(3).@Transactional(propagation=Propagation.NOT_SUPPORTED);關閉事務,不開啓事務的.spring容器默認是打開事務的.固然還有其餘一些值:(事務的傳播行爲)
REQUIRED:業務方法須要在一個事務中運行,若是方法運行時,已經處在一個事務中,那麼加入到該事務,不然爲本身建立一個新的事務(容器的默認值)
NOT_SUPPORTED:聲明方法不須要事務,若是方法沒有關聯到一個事務,容器不會爲他開啓事務,若是方法再一個事務中被調用,該事務會被掛起,在方法調用結束後,原先的事務便會恢復執行
REQUIRESNEW:屬性聲明無論是否存在事務,業務方法總會爲本身發起一個新的事務,若是方法已經運行在一個事務中,則原有事務會被掛起,新的事務會被建立,直到方法執行結束,新事務纔算結束,原先的事務纔會恢復執行
MANDATORY:該屬性指定業務方法只能在一個已經存在的事務中執行,業務方法不能發起本身的事務,若是業務方法再沒有事務的環境下調用,容器就會拋出異常
SUPPORTS:這一事務屬性聲明,若是業務方法再某個事務範圍內被調用,則方法成爲該事務的一部分,若是業務方法再事務範圍外被調用,則方法再沒有事務的環境下執行
Never:指定業務方法絕對不能在事務範圍內執行,若是業務方法再某個事務中執行,容器會拋出異常,只有業務方法沒有關聯到任何事務,才能正常執行.
NESTED:若是一個活動的事務存在,則運行在一個嵌套的事務中,若是沒有活動事務,則按REQUIRED屬性執行,它使用了一個單獨的事務,這 個事務擁有多個能夠回滾的保存點,內部事務的回滾不會對外部事務形成影響,它只對DataSourceTransactionManager事務管理器起 效.Savepoint savepoint=conn.setSavepoint();conn.rollback(savepoint);
(4).readOnly值爲事務不能修改了.timeout是事務的超時時間,isolation數據庫中的隔離級別.
(5).數據庫系統提供了四種事務隔離級別供用戶選擇,不一樣的隔離級別採用不一樣的鎖類型來實現,在四種隔離級別中,Serializable的隔離 級別最高,Read Uncommited的隔離級別最低,大多數據庫默認的隔離級別爲Read Commited,如SQLServer,固然也有少部分數據庫默認的隔離級別爲Repeatable Read,如MySql
Read Uncommited:讀未提交數據(會出現髒讀,不可重複讀和幻讀)
Read Commited:讀已提交數據(會出現不可重複讀和幻讀)
Repeatable Read:可重複讀(會出現幻讀)
Serializable:串行化
髒讀:一個事務讀取到另外一個事務未提交的更新數據
不可重複讀:在同一事務中,屢次讀取同一數據返回的結果有所不一樣,換句話說就是,後續讀取能夠讀到另外一事務已經提交的更新數據,相反,「可重複讀」在同一事務中屢次讀取數據時,可以保證所讀數據同樣,也就是,後續讀取不能讀到另外一事務已提交的更新數據.
幻讀:一個事務讀取到另外一個事務已提交的insert數據.
5、 Hibernate
1. Criteria查詢方式
(1).Criteria查詢方式(條件查詢):
Criteriac=s.createCriteria(User.class);
c.add(Restrictions.eq("name",name));//添加查詢條件,User中的name屬性的值是否等於"name"
List
list=c.list();
Useru=(User)c.uniqueResult();
2. hibernate的二級緩存配置與分析
(1).二級緩存:SessionFactory級共享:
實現爲可插拔,經過修改cache.provider_class參數來改變;hibernate內置了對 EhCache.OSCache,TreeCaceh,SwarmCache的支持,能夠經過實現CacheProvider和Cache接口來加入 Hibernate不支持的緩存實現
在hibernate.cfg.xml中加入:
<class-cacheclass="className" usage="read-only"/>或在映射文件的class元素加入子元素:
其中usage:read-only,read-write,nonstrict-read-write,transactional
Session的save(這個方法不適合native生成方式的主 鍵),update.saveOrUpdate,list,iterator,get,load,以及Query,Criteria都會填充二級緩存,但 只有(沒有打開查詢緩存時)Session的iterator,get,load會從二級緩存中取數據(iterator可能存在N+1次查詢)
Query,Criteria(查詢緩存)因爲命中率較低,因此hibernate缺省是關閉;修改cache,use_query_cache爲 true打開對查詢的緩存,而且調用query.setCaheable(true)或criteria.setCacheable(true)
SessionFactory中提供了evictXXX()方法用來清除緩存中的內容
統計消息打開generate_statistics,用sessionFactory.getSatistics()獲取統計信息,獲取統計信息成本是很高的,消耗資源.對程序的調試是頗有幫助的,能夠看到session的初始化時間,打開多少次,關閉多少次等信息.
(2).相對user對象進行緩存:
<class-cacheclass="cn.itcast.hibernate.domain.User"usage="read-only"/>只讀方式,效率高,User類不會再改變了.可以保證併發.
(3).先到一級緩存中查找,找不到在到二級緩存中查找
3.Hibernate的攔截器和監聽器
(1).攔截器和事件
攔截器與事件都是hibernate的擴展機制,Interceptor接口是老的實現機制,如今改爲事件監聽機制,他們都是hibernate的回調接口,hibernate在save,delete,update等會回調這些查詢
(2).攔截保存的的事件:
實現SaveOrUpdateEventListener接口
public classSaveListener implements SaveOrUpdateEventListener{
public voidonSaveOrUpdate(SaveOrUpdateEvent event){
if(event.getObject()instantce of cn.itcast.hibernate.domain.User){
User user =(User)event.getObject();
System.out.println(user.getName().getFirstName());
}
}
}
配置文件中:
<eventtype="save">
<listenerclass="cn.itcast.hibernate.SaveListener"/>本身定義的監聽器,不一樣監聽器的註冊順序,輸出的結果也是不一樣的.
<listenerclass="org.hibernate.evetn.def.DefaultSaveOrUpdateEventListenter"/& gt;hibernate缺省的監聽器,本身定義的監聽器會覆蓋缺省的,因此在這裏還要把缺省的監聽器註冊一下.
當保存user時,會監聽到.
4.hibernate的內部緩存分析
(1).第一級緩存是在session中,第二緩存是在sessionFactory
(2).Useruser=(User)s.get(userClass,id);
System.out.println(user.getClass());
user=(User)s.get(userClass,id);
只有一條select語句
(3).當session關閉時,緩存也就沒有數據了.
(4).緩存的做用主要用來提升性能,能夠簡單的理解成一個Map,使用緩存涉及到三個操做:把數據放入緩存、從緩存中獲取數據、刪除緩存中的無效數據
(5).一級緩存,Session級共 享,save,update,saveOrUpdate,load,get,list,iterate,lock這些方法都會將對象放在一級緩存中,一級 緩存不能控制緩存的數量,因此要注意大批量操做數據時可能形成內存溢出,能夠用evict,clear方法清除緩存的內容.
(6).只要有sql語句,就不會去緩存中拿數據,直接到數據庫中拿數據
(7).手工的對緩存中的數據進行清除.清除一條記錄:s.evict(user);清除全部的記錄s.clear();定時的清除能夠下降內存溢出的可能性.
(8).session的生命週期很短,只在一個web請求內
5.hibernate介紹與動手入門體驗
(1).模型不匹配:Java面嚮對象語言,對象模型,其主要概念有:繼承,關聯,多態等,數據庫是關係模型,其主要概念有:表,主鍵,外鍵等
(2).解決方法:第一種:使用JDBC手工轉換,第二種使用ORM框架來解決,主流的ORM框架有Hibernate、TopLink、OJB
(3).下載hibernate,將下載目錄/hibernate3.jar和/lib下的hibernate運行時必須的包
(4).配置文件hibernate.cfg.xml和hibernate.properties,XML和properties兩種,這兩個文件的做用同樣,提供一個便可,推薦XML格式,下載目錄/etc下是示例配置文件
能夠在配置文件制定:
數據庫的URL,用戶名,密碼,JDBC驅動類,方言等,啓動時Hibernate會在CLASSPATH裏找這個配置文件.映射文件(hbm.xml,對象模型和關係模型的映射),在/eg目錄下有完整的hibernate示例
(5).首先創建一個對象:
public class User{
private int id;
private String name;
private Date birthday;
//省略了get/set方法
}
編寫映射文件:User.hbm.xml
<hibernate-mappingpackage="cn.itcast.hibernate.domain">
在配置文件中hibernate.cfg.xml:
<propertyname="connection.url">jdbc:mysql://localhost:3306/jdbc
<propertyname="connection.username">root
<propertyname="connection.password">
org.hibernate.dialect.MySQLDialect
<propertyname="hbm2ddl.auto">
<mappingresource="org/hibernate/test/legacy/User.hbm.xml"/>
<class-cacheclass="org.hibernate.test.legacy.Simple" region="Simple"usage="read-write"/>
方言dialect就是哪一種數據庫.hibernate本身能夠創建表(hbm2ddl.auto)
(6).初始化:
Configuration cfg = new Configuration();
cfg.configure();
SessionFactory sf=cfg.buildSessionFactory();//SessionFactory至關於JDBC中DriverManager
Session s=sf.openSession();//工廠模式,生產connection
Transaction tx=s.beginTransaction();
User user = new User();
user.setBirthday(new Date());
user.setName("name");
s.save(user);
ts.commit();
s.close();
(7).hibernate默認會把事務自動提交功能關閉了,全部本身要手動打開,查看錶的結構命令:show create table user,看錶的引擎是否支持事務,查看引擎命令:show engines
(8).開發流程:
方式一:由Domain object->mapping->db
方式二:由DB開始,用工具生成mapping和Domain object
方式三:由映射文件
(9).hibernate管理的Domain對象類定義必需要符合JavaBean的定義規則:默認的構造方法(必須的),有無心義的標示符id(主鍵),非final的,對懶加載有影響
public class User{
private int id;
private String name;
private Date birthDay;
//get/set方法
}
10.編寫一個工具類進行初始化Hibernate
public final class HibernateUtil{
private static SessionFactorysessionFactory;
private HibernateUtil(){
}
static{//靜態代碼塊
Configuration cfg = new Configuration();
cfg.configure();//默認的傳入是hibernate.cfg.xml文件
sessionFactory = cfg.buildSessionFactory();
}
public static SessionFactorygetSessionFactory(){
return sessionFactory;
}
}
11.static void addUser(User user){//標準規範代碼
Session s=null;
Transaction tx=null;
try{
s=HibernateUtil.getSession();
tx.s.beginTransaction();
s.save(user);
tx.commit();
}catch(HibernateException e){
if(tx!=null)
tx.rollback();//不只要回滾,還有拋出異常
throw e;
}finally{
if(s!=null)
s.close();
}
}
6.hibernate配置文件中的配置項
(1).hibernate.cfg.xml和hbm.xml內容解釋:
第一:數據類型:
type能夠是hibernate、java類型或者你本身的類型(須要實現hibernate的一個接口)
第二:基本類型通常不須要在映射文件中說明,只有在一個java類型和多個數據庫數據類型相對應時而且你想要的和hiberante缺省映射不一致 時,須要在映射文件中指明類型(如:java.util.Date,數據庫 DATE,TIME,DATATIME,TIMESTAMP,hibernate缺省會把java.util.Date映射成DATATIME型),而如 果你想映射成TIME,則你必須在映射文件中指定類型
第三:數據類型的對應關係
(2).Session是非線程安全的,生命週期短,表明一個和數據庫的鏈接,在B/S系統中通常不會超過一個請求;內部維護以及緩存和數據庫鏈接,若是session長時間打開,會長時間佔用內存和數據庫鏈接
(3).SessionFactory是線程安全的,一個數據庫對應一個SessionFactory,生命週期長,通常在整個系統生命週期內有 效;SessionFactory保存着和數據庫鏈接的相關信息(user,password,url)和映射信息,以及Hibernate運行時要用到 的一些信息.
7. Hibernate映射類型
serializable:序列化到數據庫中.
8.Hibernate中使用的集合類型
(1).集合映射(set,list,array,bag,map)
List
emps = new ArrayList
();
映射文件中:
<listname="emps">
配置和set標籤是相同的,只是區分List,Set的區別
<list-indexcolumn="order_col"/>這一列是給hibernate使用的,須要記錄該員工是第幾個加進來的,即加入的順序.
(2).因爲懶加載的問題,Hibernate重寫了java中的集合類,使其具備懶加載的功能.因此在定義的時候,必需要定義成接口類型即List,Set,Map
9.hql的命名參數與Query接口的分頁查詢
(1).匿名參數:不使用佔位符了
String hql ="from User as user where user.name=:n";
query.setString("n",name);
不會依賴參數的位置
(2).Query接口中的方法
query.setFirstResult(0);
第一條記錄從哪開始,參數爲開始的位置
query.setMaxResult(10);
實現分頁功能
10.Hql與Criteria查詢的補充知識
HQL:查詢多個對象select art,user from Article art,User user where art.author.id=user.idand art.id=:id這種方式返回的是Object[],Object[0]:article,Object[1]:user;
11.Iterate查詢與N+1次查詢
(1).假設已經加入到了10個用戶
static void iterator(){
Session s=HibernateUtils.getSession();
Query q=s.createQuery("fromUser");
Iterator
users =q.iterate();
while(users.hasNext()){
System.out.println(users.next().getName().getFirstName());
}
}
首先把10個用戶的id都查詢出來,而後按照id去查詢詳細信息,這是會到一級緩存中查找,找不到在到二級緩存,找不到在到數據庫中查找.假設都到 數據庫中查詢,那麼就進行了11次查詢,第一次把全部的id都查詢,而後再逐一按照id查詢進行10次,總共進行了11次,因此在使用時必定要當心,是否 肯定一級緩存和二級緩存中有咱們想要查詢的數據,否則的話,性能就降低了
(2).在懶加載的狀況下,就會出現N+1次查詢,好比一對一:
首先查詢IdCard獲得id,而後再去訪問Person
Session s=HibernateUtil.getSession();
Query q=s.createQuery("fromIdCard");
List
ics=q.list();
for(IdCard> ic:ics){
System.out.println(ic.getPerson().getName());
}
由於懶加載,每次訪問數據的時候,都進行查詢數據庫.
12.load方法的懶加載及原理分析
(1).Useruser=(User)s.load(userClass,id);
System.out.println(user.getClass());
就是說s.load(userClass,id)返回的是User的一個代理對象.便是User的子類.在session沒有關閉前,去訪問數據庫 user.getName();可是這種方式很差,最好使用Hibernate.initialize(user);初始化懶加載.
(2).懶加載是將與數據庫的交互延遲,提升性能.load()方法,不會到數據庫查詢,只會返回一個User的一個子類.
(3).asm.jar,cglib.jar這兩個包實現懶加載,可以動態的修改內存中的字節碼.即動態的生成一個User的子類.
(4).employee.setUser(user);這是就能夠使用懶加載,創建employee和user之間個關聯,可是不須要去訪問數據庫的時候
(5).經過asm和cglib兩個包實現的,Domain是非final的,session.load懶加載
one-to-one懶加載:必須知足三個條件才能實現懶加載:第一:主表不能有constrained=true,因此主表沒有懶加載,第二:lazy!=false,第三:fetch=select;
one-to-many懶加載:第一:lazy!=false,第二:fetch=select
many-to-one:第一:lazy!=false,第二:fetch=select
many-to-many:第一:lazy!=false,第二:fetch=select
(6).可以懶加載的對象都是被改寫過的代理對象,當相關聯的session沒有關閉時,訪問這些懶加載對象(代理對象)的屬性(getId和 getClass除外),hibernate會初始化這些代理,或用Hibernate.initialize(proxy)來初始化代理對象,當相關聯 的session關閉後,再訪問懶加載的對象將出現異常.
(7).方法getId和getClass不須要訪問數據庫也是知道的,因此不是出現懶加載的初始化異常.
(8).表中的屬性也能夠使用懶加載的,只是須要在編譯後的內容進行處理,這種用途主要在字段是大文本類型時須要.
13.OpenSessionInView模式的代碼分析
(1).ThreadLocal類
private static ThreadLocal session=newThreadLocal();
線程級變量,做用域在一個線程內.
Session s=(Session)session.get();
if(s==null)}
s=getSession();
session.set(s);
}
當有一個web請求來時,服務器建立一個線程進行服務,將建立一個session,因此在這個線程內能夠訪問到session
(2).sessioncontext和事務邊界
用current_session_context_class屬性來定義context(用sessionFactory.getCurrentSession()來得到session),其值爲:
第一:Thread:ThreadLocal來管理Session實現多個操做共享一個Session,避免反覆獲取Session,並控制事務邊 界,此時session不能調用close,當commit或rollback的時候session會自動關閉 (connection.realease_mode:after_transaction).Opensession in view:在生成(渲染)頁面時保持session打開,前面所說的懶加載時,能夠保證session沒有關閉,能夠訪問到數據.
第二:由JTA事務管理器來管理事務(connection.release_mode:after_statement)
(3).用戶發送請求->web容器->doFilter(過濾器)->OpenSessionView->打開 session,事務->ActionServlet(struts)的service方法->根據配置文件找到 ->Action(execute方法)->業務邏輯層(register方法)->Dao層(addUser方法)->返回, 直到doFilter的commit,提交事務.在這個過程當中session都沒有關閉,能夠解決事務的邊界問題,解決懶加載的問題(即何時使用懶加 載).缺點:延長事務,session的生命週期,session延遲關閉,那麼一級緩存不會釋放,長時間佔用內存.客戶端的網速比較慢,致使事務和 session長時間不能關閉.即延遲關閉.會給服務器端形成很大的負載.
14.Session接口及getloadpersist方法
(1).因爲Session能夠管理多個數據庫表對應的多個實體對象,若是要查詢id爲1的實體對象,Session.get方法須要知道去哪一個數 據庫表中查詢id爲1的記錄,因此,除了給get方法傳遞所要查詢的實體對象的id值外,還必須給get方法傳遞實體對象的類型,get方法才能知道去哪 個數據庫表中進行查詢
(2).經過類的類型能夠去hibernate.cfg.xml文件中查找到對應的表
(3).在配置文件中添加標籤<propertyname="show_sql">true//能夠打印sql語句
(4).Useruser=(User)s.get(userClass,id);與User user=(User)s.load(userClass,id);的區別,load不會去訪問數據庫,只有第一次訪問時,纔會訪問數據庫.增長一條打印 出user1的類名的代碼,就能夠看到load方法所返回的User子類的名稱了,該語句以下:
System.out.println(user1.getClass().getName());
(5).s.save(user)和s.persist(user);都是存儲數據,persist方法沒有sql語句,沒有開啓事務,save會回滾,persist不會回滾
15.Session與SessionFactory的多線程問題
Session內部封裝了一個connection對象,儘可能遲的建立鏈接,儘可能早的釋放鏈接
16.本地sql查詢與命名查詢
(1).使用Query接口
static list sql(){
Session s=HibernateUtil.getSession();
Query q = s.createSQLQuery("select * fromuser").addEntity(User.class);//查詢的結果是User對象
List rs=q.list();
for(User r:rs){
System.out.println(r.getName());
}
}
(2).不一樣的數據庫,本地的查詢語句是不一樣的,因此這種本地的查詢語句最好不要使用,兼容性和移植性很差.
(3).命名查詢:將查詢語句放在配置文件中,之後修改查詢語句只修改配置文件中的查詢語句就能夠了.
<queryname="getUserByBirthday">
<[CDATA[from User wherebirthday=:birthday]]>
這個定義能夠放到class標籤內部,不須要使用全名,只須要getUserByBirthday便可,可是在這個範圍內,不能出現重名,若是在外部,那就須要全名了,cn.itcast.hibernate.domain.User.getUserByBirthday
在配置文件中
static List namedQuery(){
Session s=HibernateUtil.getSession();
Queryq=s.getNamedQuery("getUserByBirthday");
q.setDate("birthday",new Date());
return q.list();
}
(4).hibernate能夠作到用Map代替Domain對象,存入到數據庫,可是這就符合ORM定義了,同時也能夠將數據庫中的內容轉換XML
17.多對多關聯關係的查詢
使用表之間的關聯join,效率低
18.多對多關聯關係的映射與原理分析
(1).多對多(teacher-student):在操做和性能方面都不太理想,因此多對多的映射使用較少,實際使用中最好轉換成一對多的對象模型;Hibernate會爲咱們建立中間關聯表,轉換成兩個一對多.
ER圖:teacher:id(PK);student:id(PK);teacher_student:teacher_id(PK,FK1),student_id(PK,FK2)
(2).
public class Teacher{
private int id;
private String name;
private Set
students;
//省略get/set方法
}
public class Student{
private int id;
private String name;
private Set
teachers;
//省略get/set方法
}
teacher的映射文件:
根據student_id去查詢學生的相關信息
同理student的映射文件類似.
(3).測試類:
Set
ts=newHashSet
();
Teacher t1=new Teacher();
t1.setName("t1 name");
Teacher t2=new Teacher();
t2.setName("t2 name");
ts.add(t1);
ts.add(t2);
Set
ss=newHashSet
();
Student s1=new Student();
s1.setName("s1");
Student s2=new Student();
s2.setName("s2");
t1.setStudents(ss);//創建關聯關係
t2.setStudents(ss);
ss.add(s1);
ss.add(s2);
s.save(t1);
s.save(t2);
s.save(s1);
s.save(s2);
在中間表中插入數據
19.多對一的懶加載分析
(1).查詢員工的信息時,是否須要部門的信息,默認的狀況下是懶加載的方式,怎樣判斷是否進行了懶加載,能夠經過打印出的sql語句中的查詢語句便可
(2).當IdCard中的id是主鍵也是外鍵,當id有值時,必定有一個person與之對應,因此能夠使用懶加載,先生成一個代理對象,當須要 person的信息時,纔去查詢,反過來,由於person中的id只是個主鍵,知道person的id,IdCard中不必定有一個值與之對應,因此不 使用懶加載的方式,而是直接去查詢數據庫,這就是查詢主表時不使用懶加載,查詢從表時使用懶加載.
(3).可是多對一的部門和員工,直接就是用了代理,depart.getEmps()獲取員工時,Hibernate中的集合把集合空對象和空集合是相同的概念.
20.多對一關聯關係的檢索與原理分析
(1).查詢操做(department表的查詢和之前同樣,只是employee表不同):
static Employee query(int empid){
Employee emp =(Employee)s.get(Employee.class,empid);
System.out.println("departname:"+emp.getDepart().getName());//獲得department的名稱.
return emp;
}
進行兩次查詢,首先根據id查詢employee表,獲得depart_id,在根據depart_id查詢department表.
21.多對一關聯關係的映射與原理分析
(1).多對一:映射文件:
ER圖中定義Employee主鍵(PK):id和外鍵(FK):depart_id,Department的主鍵id;
(2).創建Department類
public class Department{
private int id;
private String name;
//省略get/set方法
}
創建Employee類
public class Employee{
private int id;
private String name;
private Department depart;
//省略get/set方法
}
(3).映射文件:
<hibernate-mappingpackage="cn.itcast.hibernate.domain">
不在使用標籤property,是對象類型depart,使用標籤
經過反射能夠找到depart對應的映射文件,當depart_id與depart映射文件中的id相同時,就查找到了.也能夠使用屬性not-null="true",設置colum="depart_id"這列不爲空
(4).column="depart_id"不設置能夠,默認就是column="depart"
(5).staticDepartemnt add(){
//模板代碼,省略
Department depart = new Department();
depart.setName("depart name");
Employee emp = new Employee();
emp.setDepart(depart);//直接賦值就能夠了,只要在對象創建關係,數據庫中的表就創建關係了.
emp.setName("emp name");
s.save(depart);
s.save(emp);
return depart;
}
(6).當s.save(depart);與s.save(emp)兩條語句的順序調換,會多出現一條更新語句,由於首先存儲emp,當存儲到 depart時,由於employee中定義了department,因此hibernate檢測到employee中的depart發生改變了,就進行 了更新操做.此時是持久態
22. 分佈式緩存的分析
大型網站有多個服務器,即就有多個cache,每一個服務器對應一個cache,
23.關聯關係的級聯操做
(1).cascade和inverse:
Casade用來講明當對主對象進行某種操做時是否對其關聯的從對象也作相似的操做,經常使用的cascade:none,all,save- update,delete,lock,refresh,evict,replicate,persist,merge,delete- orphan(one-to-many),通常對many-to-one,many-to-many不設置級聯,在one-to-one和one-to- many中設置級聯
Inverse表「是否放棄維護關聯關係」(在Java裏兩個對象產生關聯時,對數據庫表的影響),在one-to-many和many-to- many的集合定義中使用,inverse="true"表示該對象不維護關聯關係;該屬性的值通常在使用有序集合時設置成false(注意 hibernate的缺省值是false),one-to-many維護關聯關係就是更新外鍵,many-to-many維護關聯關係就是在中間表增減記 錄
注:配置成one-to-one的對象不維護關聯關係.
24.緩存的原理與模擬分析
(1).第一我的讀的信息和後一我的讀的信息可能相同,那第二我的讀信息時可以加快速度了.
(2).第二人讀取信息時,就不是到數據庫中讀取了,能夠到緩存中讀取數據.
(3).使用緩存cache存取數據.
25.繼承_鑑別器與內鏈接器相結合
(1).子類的特有屬性不少,就拿一張表進行對應,特有屬性少的就和父類放在同一個表中,
(2).employee:id(PK),name,depart_id,type,skill;sales:employee_id(PK,FK),sales;
Skiller子類和Employee父類放在一塊兒,Sale類本身對應一張表.
(3).映射文件中只需按照前兩中方式進行改變.
26.繼承_每一個具體類映射一張獨立表
(1).沒有公共的屬性,全部的屬性都是本身特有的,在插入時候不須要涉及到多個表的關聯了,效率高.若是employee不是抽象的,會有employee表
(2).employee:id(PK),name,depart_id;skiller:id(PK),name,skill,depart_id;sales:id(PK),name,sell,depart_id;
(3).映射文件:
(4).在查詢的時候,多態查詢時,仍是要進行三種表的關聯查詢,可是插入只在一張表進行.
27.繼承關係_每一個類映射到一張表
(1).employee:id(PK),name,depart_id;sales:employee_id(PK,FK),sell;skiller:employee_id(PK,FK),skill;
(2).此時不須要鑑別器了,每一個子類對應一張表
(3).映射文件:
<joined-subclassname="Skiller" table="skiller">
(4).插入子類時,相同的屬性插入到employee表中,本身特有的屬性插入到本身表中,若是插入一個技術員Skiller(name,skill)時skill插入skiller表中,name插入employee表中,這時就插入了兩張表.
(5).當查詢本身特有的屬性時,會關聯兩張表,當查找相同的屬性時,會關聯三張表.因此查詢時效率低.不要進行多態查詢,最好查詢具體的子類:
具體查詢:Employee emp = (Employee)s.getId(Skiller.class,id);
多態查詢:Employee emp = (Employee)s.getId(Skiller.class,id);
28.繼承關係_整個繼承樹映射到一張表
(1).public classSkiller extends Employee{
private String skill;
//省略get/set方法
}
public class Sales extends Employee{
private int sell;
//省略get/set方法
}
(2).employee表中的字段:id(PK),name,depart_id,type(區分不一樣類型的員工,又稱鑑別器),skill,sell
(3).這種方式當增長子類時,須要修改employee表結構.
(4).映射文件:
鑑別器,hibernate用來區分不一樣的子類.
(5).將一棵繼承樹映射到一張表中,因此在查詢時,只對一張表進行操做,效率高,可是不靈活,當增長子類時,須要更改表結構,同時每一個字段不能設置成非空約束.
29.實體對象的三種狀態與saveOrUpdate方法
(1).Session的幾個主要方法:
第一:save,persist保存數據,persist在事務外不會產生insert語句
第二:delete:刪除對象
第三:update:更新對象,若是數據庫中沒有記錄,會出現異常
第四:get:根據ID查詢數據,會馬上訪問數據庫
第五:load:根據ID查詢,(返回的是代理,不會當即訪問數據庫)
第六:saveOrUpdate,merge(根據ID和version的值來肯定是save或update),調用merge你的對象仍是託管的
第七:lock(把對象編程持久對象,但不會同步對象的狀態)
(2).瞬時(transient):數據庫中沒有數據與之對應,超過做用域會被JVM垃圾回收器回收,通常是new出來且與session沒有關聯的對象
持久(persistent):數據庫中有數據與之對應,當前與session有關聯,而且相關聯的session沒有關閉,事務沒有提交;持久對象狀態發生改變,在事務提交時會影響到數據庫(hibernate能檢測到)
脫管(detached):數據庫中有數據與之對應,但當前沒有session與之關聯;託管對象狀態發生改變,hibernate不能檢測到.
(3).當關閉session時,持久態就變成了脫管狀態了,區分這三種狀態的兩個標準:是否與數據庫中記錄相對應,是否在session中.
(4).當在脫管的狀態時,更新的時候須要執行update的更新語句,由於不在session中.
(5).對象new是瞬時的,get(),load(),find(),iterate()等是持久的,瞬時狀態執行 save(),saveOrUpdate()時變成持久的,當持久狀態執行delete()時變成瞬時的,當脫管狀態執行 update(),saveOrUpdate(),lock()時變成持久狀態,當持久狀態執行evict(),close(),clear()時,持久 狀態變成脫管狀態.
(6).瞬時對象的id沒有值,脫管對象的id是有值的.因此當沒有值時執行save()方法,當有值時執行update()方法.
30.實體類或屬性名與數據庫關鍵字衝突問題
使用Oracle時,user是個關鍵字,可能出現問題,將表名添加反引號.
31.使用Hibernate完成CRUD實驗的步驟說明
(1).實驗步驟:
第一步:設計domain對象User
第二步:設計UserDao接口
第三步:加入hibernate.jar和其依賴的包
第四步:編寫User.hbm.xml映射文件,能夠基於hibernate/eg目錄下的org/hibernate/auction/User.hbm.xml修改
第五步:編寫hibernate.cfg.xml配置文件,能夠基於hibernate/etc/hibernate.cfg.xml修改;必須提 供的幾個參數:connection.driver_class、connection.url、connection.username、 connection.password、dialect、hbm2ddl.auto
第六步:編寫HibernateUtils類,主要用來完成hibernate初始化和提供一個得到Session的方法
第七步:實現UserDao接口
32.事務的悲觀鎖和樂觀鎖
(1).悲觀鎖和樂觀鎖
悲觀鎖由數據庫來實現;樂觀鎖hibernate用version和timestamp來實現,悲觀鎖就至關於寫鎖,當本身在操做時,別人不能進行任何操做,
(2).可能多我的來讀取同一個數據源,可能後一我的修改後的結果覆蓋前一我的修改的結果,存在併發問題
(3).悲觀鎖是不可取的,咱們給每條記錄添加一個版本號,當同時操做數據源時,判斷版本號,若是版本號不符合,就不進行更新.假設剛開始版本號爲 0,同時來兩我的進行操做,判斷版本號是否爲0,若是爲0,就進行操做,操做完後版本號加一,那第二我的就發現版本號不等於0,就不會進行操做了,也不會 覆蓋前一我的進行的操做.
(4).在映射文件中:
<versionname="ver"/>該標籤必須在id標籤的下面,便是id的子標籤.
(5).版本號的類型是整型的,也能夠是日期型的
(6).
Session s1=HibernateUtil.getSession();
Transactiontx1=s1.beginTransaction();//第一個線程操做事務
User user1=(User)s1.get(User.class,id);
Session s2 =HibernateUtil.getSession();
Transactiontx2=s2.beginTransaction();//第二個線程操做事務
User user2=(User)s2.get(User.class,id);
user1.getName().setFirstName("firstName1");
user2.getName().setFirstName("firstName2");
tx2.commit();//線程二先提交,成功了
tx1.commit();//線程一提交不成功.由於版本號不同.
33.事務與事務邊界的相關知識
(1).一個SessionFactory對應一個數據庫,由JDBC實現
(2).事務的控制應該在業務邏輯層實現.可是事務的對象是在DAO層,那麼在業務邏輯層中調用事務的對象,就出現了耦合,因此要解決這個耦合,就需藉助第三方架包了EJB,Spring
34.完善HibernateUtil類及hql查詢入門
(1).HQL:面向對象的查詢語言,與SQL不一樣,HQL中的對象名是區分大小寫的(除了Java類和屬性其餘部分不區分大小寫),HQL中查的 是對象而不是和表,而且支持多態;HQL主要經過Query來操做,Query的建立方式:Query q=session.createQuery(hql);
from Person
from User as userwhere user.name=:name//其中User是類不是表名,user是別名
form User as userwhere user.name=:name and user.birthday<:birthday
(2).Criteria:是一種比HQL更面向對象的查詢方式;Criteria的建立方式:Criteria crit=session.createCriteria(DomainClass.class);
簡單屬性條件如:
criteria.add(Restrictions.eq(propertyName,value)),criteria.add(Restrictions.eqProperty(propertyName,otherPropertyName))
(3).public staticvoid add(Object entity){//可以保存全部對象
Session s=null;
Transactiontx=null;
try{
s=HibernateUtil.getSession();
tx.s.beginTransaction();
s.save(entity);
tx.commit();
}catch(HibernateExceptione){
if(tx!=null)
tx.rollback();//不只要回滾,還有拋出異常
throw e;
}finally{
if(s!=null)
s.close();
}
}
同理更新,刪除同時一樣的道理
(4).執行HQL語句
Session s=null;
try{
s=HibernateUtil.getSession();
Stringhql="from User as user where user.name=?";
Queryquery=s.createQuery(hql);
query.setString(0,name);//替換佔位符
Listlist=query.list();//JDBC中的executQuery()相似
for(Useruser:list{
System.out.println(user.getName());
}
//Object obj=query.uniqueResult();當肯定返回值只有一個的時候,使用這種方法.當查詢有多個結果時,會出現異常
}finally{
if(s!=null)
s.close();
}
支持多態,查詢的話,子類對應數據庫表也被查詢,若是from Object的話,會把數據庫中的表都查一遍,由於全部的類都是Object的子類.
35.一對多關聯關係的映射與原理分析
(1).在Department的角度上是否是一對多了,在Department中定義:
privateSet
emps;//一個department中有多個員工
(2).映射文件:
<classname="Department">
<idname="id">
<generatorclass="native"/>
<propertyname="name"/>
<setname="emps">用set屬性進行映射
<keycoluem="depart_id"/>設置外鍵
<one-to-manyclass "Employee"/>
(3).System.out.println("empsize:"+depart.getEmps().size());
打印出department中全部的employee人數.
(4).首先添加employee,在添加到department,即告訴employee屬於哪一個department;多兩條更新語句.
Set
emps = new HashSet
();
emps.add(emp1);
emps.add(emp2);
depart.setEmps(emps);
告訴department有哪些employee
emp1.setDepart(depart);
emp2.setDepart(depart);
(5):ER圖:Deparment:id(PK);Employee:id(PK),depart_id(FK1);
36.一對多和多對多的懶加載分析
(1).對於one-to-one懶加載方式體現出的效率不是很明顯,查詢身份證號時,把person的信息也查詢出來,沒有查詢太多的信息,對效率的影響不是很大
(2).對於one-to-many懶加載方式就體現的很明顯的了,當咱們查詢部門的詳細信息時,可能把該部門的全部員工都查詢出來,由於一個部門可能有不少員工,因此這時效率就明顯下降了.
(3).缺省的是懶加載,當depart.getEmps()時,纔會查詢員工的信息,由於java中的set集合沒有懶加載的功能,當咱們的代碼 只是獲取集合代理對象的引用,比沒有調用該集合代理對象的方法,因此,hibernate在這裏還用不着去查詢數據庫來填充集合代理,所以不會拋出"代理 未初始化"的異常,若是將代碼改成depart.getEmps().size(),就能夠看到異常了.
(4).對於many-to-many方式懶加載也很重要,由於涉及到三張表的查詢.因此也須要懶加載的功能.
37.一對一的懶加載分析
(1).one-to-one在查詢主對象的時候默認狀況下不使用懶加載,使用一個關聯查詢.可是在查詢從對象的時候使用了懶加載.
(2).constrain=true是創建外鍵約束
(3).lazy="proxy",使用懶加載,默認的值也是proxy,還有false,true的取值
(4).fetch="join",使用什麼方式去抓取,默認值爲select,join是一次查詢(表的鏈接),select是兩次查詢.當lazy="proxy"時,fetch="join"是無效的,它們倆之間的設置是互斥的.
38.一對一外鍵關聯關係的映射與原理分析
(1).一對一:基於外鍵的one-to-one,能夠描述爲多對一,加上unique="true"約束
<many-to-onename="person" column="person_id" unique="true"not-null="true"/>區別於多對一.只需將外鍵設置爲惟一.
(2).對於IdCard的映射文件,其的id不是外部生成的,而是自增加的.
<generatorclass="native"/>對於Person的映射文件:
39.一對一主鍵關聯關係的檢索
(1).查詢主對象:
Personp=(Person)get(Person.class,id);
System.out.println(p.getIdCard().getId());
理論上是兩次查詢,可是實際只進行了一次查詢,使用了表之間的關聯join,效率上比兩次查詢高
查詢從對象:
IdCardidCard=(IdCard)get(IdCard.class,id);
System.out.println(idCard.getPerson().getId());
理論上和實際上都進行了兩次查詢
40.一對一主鍵關聯關係的映射與原理分析
(1).基於主鍵的one-to-one(person的映射文件)
<generatorclass="foregin"><paramname="property">idCard
(2).對象模型:
public class Person{
private int id;
private String name;
private IdCard idCard;
//省略get/set方法
}
public class IdCard{
private int id;
private String name;
private Person person;
//省略get/set方法
}
(3).Person的映射文件:
IdCard的映射文件:
主鍵是由外部獲得,不是本身獲得的
<paramname="property">personIdCard的id是由person獲得的
添加約束,配置外鍵.
idcard中的主鍵是person中的外鍵
(4).測試代碼:
IdCard idCard = new IdCard();
Person p=new Person();
p.setName("p1");
p.setIdCard(idCard);
idCard.setPerson(p);
s.save(p);
s.save(idCard);
IdCard中的id是由Person獲得的.只有主對象(person)存在,從對象(idcard)存在.
(5).ER圖:person:id(PK);IdCard:id(PK,FK)
41.組件關聯關係的映射與原理分析
(1).組件映射(User-Name):關聯的屬性是個複雜類型的持久化類,但不是實體即:數據庫中沒有表與該屬性對應,但該類的屬性要之久保存的
當組件的屬性不能和表中的字段簡單對應的時候能夠選擇實現:
org.hibernate.usertype.UserType或
org.hibernate.usertype.CompositeUserType
(2).用戶名name是個對象類型
public Class Name{
private String firstName;
private String lastName;
//get/set方法省略
}
想過使用一對一.一對多,多對一均可以,一我的只能有一個名字,一我的能夠有多個名字.這是數據庫中確定有兩張表:User和Name,可是如今 Name的內容很小,不想設計成一個實體,不想在數據庫中對應一張表,由於它過小了,此時就是用組件相關聯,將用戶user和名字name設計到同一張表 中
6、 JDBC
1.DAO設計思想與搭建骨架
(1).創建Domain包,在包中創建一個實體對象(bean).
public class User{
private int id;
private String name;
private Date birthday;//java.util.Date
private float money;
//生成對應的get/set方法,省略
}
(2).定義Domain接口:
public interface UserDao{
public void addUser(User user);
public User getUser(int userid);
public void update(User user);
public void delete(User user);
public User findUser(StringloginName,String password);
}
這個接口是給service層使用的.
(3).實現UserDao接口
public class UserDaoImpl implements UserDao{
public void addUser(User user){};
public User getUser(int userid){};
public void update(User user){};
public void delete(User user){};
public User findUser(StringloginName,String password){};
}
(4).在UserDaoImpl中拋出的異常進行包裝,定義一個異常類
(5).工廠模式:UserDao userDao = DaoFactory.getInstance().getUserDao();
2.Java的動態代理及使用該技術完善鏈接代理
(1).前面說到的靜態代理模式,有點麻煩,由於須要實現接口Connection的全部方法.
(2).public classMyConnectionHandler implements InvocationHandler{
private Connection realConnection;
MyConnectionHandler(){
}
Connectionbind(Connection realConn){//經過此方法將鏈接傳進來
ConnectionwarpedConnection = (Connection)Proxy.newProxyInstance(this.getClass().getClassLoader(),newClass[] {Connection.class},this);//動態的編寫一個類,這個類實現Connection接口,最終會把Connection的全部方 法都交給InvocationHandler處理器處理,在內存中直接產生一個字節碼.
returnwarpedConnection;
}
public Objectinvoke(Object proxy,Method method,Object[] args){
if("close".equals(method.getName())){//是close方法
this.dataSource.connectonsPool.addList(this.warpedConnection);
}
returnmethod.invoke(this.realConnection,args);
}
}
這就是動態代理模式,無論是動態的,仍是靜態的,最終到底都是關心操做Connection的方法.
3.JdbcTemplate類中的其餘各個查詢方法
(1).Spring的JdbcTemplate
第一:查詢帶有參數,和行映射方法:
public ObjectqueryForObject(String sql,Object[]args,RowMapper rowMapper),使用自定義的UserRowMapper完成映射
一個RowMapper的經常使用實現BeanPropertyRowMapper,該實現可將結果集轉換成一個Java Bean(字段名與Java Bean屬性名不符合規範,可用別名處理)返回一條記錄.
第二:public List query(String sql,Object[]args,RowMapperrowMapper)返回多條記錄
第三:public int queryForInt(String sql)(如:selectcount(*) from user),其餘結果好比String可用queryForObject方法向下轉型
public MapqueryForMap(String sql,Object[]args)返回不是對象類型的Map(key:字段名或別名,value:列值);當查詢的結果不是一個對象時,就是用一個 Map進行存放結果.查詢共多少條記錄,最大值,最小值等信息時,當返回的是String類型時,就是用queryForObject(String sql);只是要對返回類型進行轉換.
第四:public List queryForList(String sql,Object[]args)返回多個Map
4.JDBC的理論概述
(1).JDBC(Java數據庫鏈接)由一些藉口和類構成的api,j2se的一部分,由java.sql,javax.sql包組成
(2).應用程序、JDBC API、數據庫驅動及數據庫之間的關係:
應用程序-->JDBC-->MySql Driver,Oracle Driver,DB2Driver--->MySql,ORacle,DB2
5.jdbc中數據類型與日期問題
(1).rs.getInt("id"),getString("name"),rs.getDate("birthday"),rs.getFloat("money")),不一樣的類型的獲取數據.
(2).java.sql.Date是繼承java.util.Date,java.util.Date是日期和時間的,而java.sql.Date只有日期,而沒有時間.
(3).不能將java.util.Date賦給java.sql.Date,所 以:newjava.sql.Date(birthday.getTime()));這樣就能夠將java.util.Date轉換成 java.sql.Date,java.sql.Date直接賦給java.util.Date能夠的.
(6).st.executeUpdate(sql),帶參數的方法是Statement的,不帶參數的方法是PreperedStatement的
6.JTA分佈事務的簡要介紹
(1).跨多個數據源的事務,使用JTA容器實現事務,分紅兩個階段提交
javax.transaction.UserTransactiontx=(UserTransaction)ctx.lookup("jndiName");
tx.begin();//connection1,connection2(可能來自不一樣的數據庫)
tx.commit()//tx.rollback();
(2).tomcat不支持這種容器.weblogic能夠支持.
(3).第一階段:向全部的數據庫提交事務的請求,當有事務回滾的請求,全部的數據庫都回滾,第二階段:當沒有回滾請求,就進行提交事務.
(4).分佈式事務處理.
7.Statement的sql注入問題
(1).SQL注入:PreparedStatement和Statement:
在sql中包含特殊字符或SQL的關鍵字(如:'or 1 or')時,Statement將出現不可預料的結果(出現異常或查詢的結果不正確),可用PreparedStatement來解決
PreperedStatement(從Statement擴展而來)相對Statement的優勢:
第一:沒有SQL注入的問題
第二:Statement會使數據庫頻繁編譯SQL,可能形成數據庫緩衝區溢出
第三:數據庫和驅動能夠對PreperedStatement進行優化(只有在相關聯的數據庫鏈接沒有關閉的狀況下有效)
PreparedStatement是Statement的子接口.
(2).
PreparedStatementps=null;//預處理接口,須要進行預處理,因此在構造的時候就須要SQL語句了,ps=conn.prepareStatement(sql);而Statement是在查詢的時候須要SQL語句.
Stringsql="select id,name from user where name=?";?問號是佔位符
ps.setString(1,name);將傳過來的name參數替換第一個佔位符?,在此過程當中,將name進行的處理,將特殊符號去除,當執行查詢時,不須要SQL語句了,否則會報錯,rs=ps.executeQuery();
(3).創建鏈接最消耗時間的,當程序執行屢次時,PreperedStatement比Statement除去創建鏈接的時間,前者效率高.
8.編寫一個基本的鏈接池來實現鏈接的重複使用
(1).鏈接池常用到插入和刪除,因此使用LinkedList,
public class MyDataSource{
private LinkedList
connectionsPool = new LinkedList
();
public MyDataSource(){
for(int i=0;i<10;i++){//開始時建立10個鏈接
this.connectionsPool.addLast(this.createConnection());
}
}
public Connection createConnection() {//建立鏈接
returnDriverManager.getConnection(url,user,password);
}
public Connection getConnection(){//獲取鏈接
return this.connectionPool.removeFirst();
}
public void free(Connection conn){//釋放鏈接
this.connectionsPool.addList(conn);
}
}
獲得鏈接並非重複的.想重複的拿取鏈接,建立的鏈接數n<用戶取鏈接數m,便可.
(2).
private static int initCount=5;//定義初始化鏈接數
private static int maxCount=10;//最大鏈接數
private static int currentCount=0;//當前建立的鏈接數
(3).爲了保證併發操做,須要在獲取鏈接中同步:
synchronized(connectionsPool){
if(this.connctionPool.size()>0)//鏈接池中還有鏈接
return this.connectionsPool.removeFirst();
if(this.currentCount<maxCount)//當前鏈接數沒有超過最大鏈接數,能夠接着建立鏈接,
return this.createConnection();
throw new SQLException("已經沒有鏈接了");//超過了最大鏈接數,拋出異常.
}
9.編寫一個簡單的JDBC的例子
(1).鏈接數據的步驟:
第一步:註冊驅動(只作一次)
DriverManager.registerDriver(newcom.mysql.jdbc.Driver());
第二步:創建鏈接(Connection)
Connectionconn=DriverManager.getConnection("jdbc:mysql://localhost:3305/jdbc","root","");沒有密碼
第三步:建立執行SQL的語句(Statement)
Statementst=conn.createStatement();
第四步:執行語句
ResultSet rs =st.executeQuery("select * from user");
第六步:處理執行結果(ResultSet)
while(rs.next()){//遍歷行
System.out.println(rs.getObject(1)+'\t'+rs.getObject(2));//第一列,第二列
}
第七步:釋放資源
rs.close();//關閉資源和打開資源的順序是相反的
st.close();
conn.close();
10.參數的元數據信息
(1).
Connection conn=JdbcUtils.getConnection();
PreparedStatementps=null;
ResultSet rs=null;
ps.conn.prepareStatement(sql);//sql中可能含有參數(佔位符),Object[]params存儲參數,能夠動態的查看sql中含有哪些參數.
ParameterMetaDatapmd=ps.getParameterMetaData();
intcount=pmd.getParameterCount();//獲得參數的個數
for(inti=1;i<count;i++){
System.out.println(pmd.getParameterClassName(i));//獲得參數的類名
System.out.println(pmd.getParameterType(i));//獲得參數的類型
ps.setObject(i,parames[i-1]);//遍歷替換參數
}
String sql ="select * from user where name=? and birthday<? and money>?";
直接返回的類型都是String,VARCHAR
11.分析jdbc程序的編寫步驟和原理
(1).鏈接是經過底層的TCP/IP協議進行的
(2).註冊驅動:
方式一:Class.forName("com.mysql.jdbc.Driver");
推薦這種方式,不會對具體的驅動類產生依賴,類加載到內存中,會調用靜態代碼塊:
static{
try{
DriverManager.registerDriver(new Driver());
}catch(SQLException e){
throws RuntimeException();
}
}
方式二:DriverManager.registerDriver(newcom.mysql.jdbc.Driver());
會形成DriverManager中產生兩個同樣的驅動,並會對具體的驅動類產生依賴,其內部定義了一個Vector列表,將多個驅動存放到Vector中
方式三:System.setProperty("jdbc.drivers","driver1:driver2");
雖然不會對具體的驅動類產生依賴;但註冊不太方便,因此不多使用,能夠註冊多個驅動
(3).方式一接受的是一個字符串,方式二接受的是一個驅動類,因此具備依賴關係
(4).建立鏈接:
Stringurl="jdbc:mysql://localhost:3394/jdbc";
格式:jdbc:子協議:子名稱//主機名:端口/數據庫名
String user="root";
String password="";
Connectionconn=DriverManager.getConnection(url,user,password");
(5).釋放資源:數據庫創建鏈接的個數也是有限制的,當數據庫建立了多個鏈接,數據庫可能運行的很慢,可能致使數據庫崩潰,佔用系統資源.
12.分析在實際項目中該如何應用JDBC
(1).三層架構:
表示層:基於web的jsp、servlet、struts、webwork、spring web MVC等,基於客戶端的swing,swt等
業務邏輯層:Pojo(service,manager),Domain,session EJB、spring
數據訪問層:JDBC,IBatis,Hibernate,JDO,Entity Bean
層與層之間用接口隔離
13.規範和封裝JDBC程序代碼
(1).規範的代碼:
Stringurl="jdbc:mysql://localhost:2332/jdbc";
String user="root";
String password="";
Statement st=null;
ResultSet rs=null;
Connecton conn=null;
try{
Class.forName("com.mysql.jdbc.Driver");
conn=DriverManager.getConnection(url,user,password);
st = conn.createStatement();
rs=st.executeQuery("select * fromuser");
}finally{
try{
if(rs!=null)
rs.close();
}finally{if(st!=null)
try{
st.close();
}finally{
if(conn!=null)
conn.close();
}
}
}
}
(2).設計一個工具類:
public final class JdbcUtils{
private static Stringurl="jdbc:mysql://localhost:2332/jdbc";
private static String user="root";
private static String password="";
private JdbcUtils(){//不容許實例化
}
static{//驅動只註冊一次
try{
Class.forName("com.mysql.jdbc.Driver");
}catch(ClassNotFoundException e){
throw new ExceptionInitializerError(e);
}
}
public static Connection getConnection(){//建立鏈接
returnDriverManager.getConnection(url,user,password);
}
public static void free(ResultSetrs,Statement st,Connection conn){//釋放資源
try{
if(rs!=null)
rs.close();
}catch(SQLException e){
e.printStackTrace();
}finally{if(st!=null)
try{
st.close();
}catch(SQLException e){
e.printStackTrace();
}finally{
if(conn!=null)
conn.close();
}
}
}
}
}
14.將Dao中的修改方法提取到抽象父類中
(1).對於代碼的重構,焦點就是將代碼變化的部分和不變的部分分離開來.一個是sql語句不一樣,參數不一樣,提取一個超類,相同的部分,放到超類中,不一樣的部分由子類實現.
publci abstract class AbstractDao{
public void update(String sql,Object[]args){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
conn=JdbcUtils.getConnection();
ps=conn.prepareStatement(sql);
for(int i=0;i<args.length;i++){//用args參數列表,更新數據
ps.setObject(i+1,args[i]);
}
}
//args就是參數列表,
}
public class UserDaoImpl extendsAbstractDao{
public void update(User user){
String sql="update user setname=?,birthday=?,money=?,where id=?";
Object[] args=new Object[]{user.getName(),user.getBirthday(),user.getMoney(),user.getId()};
super.update(sql,args);//調用父類的update方法.
}
}
15.可更新和對更新敏感的結果集
(1).
st=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,Result.CONUR_UPDATABLE);
在讀取數據時,能夠更改數據,可更新結果集.
(2)
while(rs.next()){
rs.getObject("name");
rs.getObject("money");
String name= rs.getString("name");
if("lisi".equals(name)){
rs.updateFloat("money",200f);
rs.updateRow();//更新行
}
}
(3).這種方式不經常使用,在查詢時,把數據更改了,給人一種不明確感.
在查詢結果集時,更新數據時數據庫能不能感知到數據更新了.數據庫敏不敏感SENSITIVE
16.利用結果集元素數據將查詢結果封裝爲map
(1).將查詢結果放在map中
key:列的名稱
value:列的值
(2).
Connection conn=JdbcUtils.getConnection();
ps=conn.prepareStatement(sql);
rs=ps.executeQuery();
ResultSetMetaData rsmd =rs.getMetaData();//獲取結果集的元數據
int count= rsmd.getColumnCount();//結果有多少列
for(int i=1;i<=count;i++){//循環遍歷列
System.out.println(rsmd.getColumnClassName(i));//每一列的類型名
System.out.println(rsmd.getColumnName(i));//每一列的名稱
System.out.println(rsmd.getColumnLabel(i));//每一列的別名
}
這裏就能夠準確的獲得每一列的類型,而不像前面的都是String類型.
String[]colName=new String[count];//存放每一列的名稱
Map<String,Object> data=null;
while(rs.next()){//按行循環
data = new HashMap<String,Object>();
for(int i=0;i<colNames.length;i++){//按列循環
data.put(colNames[i],rs.getObject(colNames[i]));//根據類名獲得列的值
}
}
靈活性很是高.可以按照各類方式查詢
16.如何使用開源項目DBCP
(1).dbcpconfig.properties數據源的配置文件:
鏈接配置:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=root
初始化鏈接:
initialiSize=10
最大鏈接數量:
maxActive=50
最大空閒鏈接://不一樣的時間段建立的鏈接不一樣,可能出現空閒鏈接.
maxIdle=20
最小空閒鏈接:
minIdle=5
超過等待時間以毫秒爲單位 6000毫秒/1000等於60秒
maxWait=60000
//當沒有鏈接可取的時候,讓當前線程等待一段時間,在去拿鏈接
JDBC驅動創建鏈接時附帶的鏈接屬性,屬性的格式必須爲這樣:[屬性名=property;],注意:"user" 與"password"兩個屬性會被明確地傳遞,所以這裏不須要包含它們(url後面攜帶的值)
connectionProperties=userUnicode=true;characterEncoding=gbk
指定由鏈接池所建立的鏈接的自動提交狀態
defaultAutoCommit=true
driver default指定由鏈接池所建立的鏈接的只讀(read-only)狀態,若是沒有設置該值,則"setReadOnly"方法將不被調用,(某些驅動並不支持只讀模式,如:Informix)
defaultReadOnly=
driver default指定 由鏈接池所建立的鏈接事務級別(TransactionIsoation),可用值爲下列之一(詳情可見javadoc)NONE,READ,UNCOMMITTED,READ_COMMITTE
defaultTransactionIsolation=READ_UNCOMMITTED
(2).DBCP是apache的開源項目.實現了DataSource接口.Data Base Connection Pool,修改代碼須要重新編譯,打包,修改配置文件只須要重啓便可.
(3).
Properties prop = new Properties();
InputStream is =JdbcUtils.class.getClassLoader().getResource.AsStream("dbcp.property");//讀取property文件
prop.load(is);
private static DataSoruce myDataSource=null;
myDataSource=BasicDataSourceFactory.createDataSource(prop);
(4).DataSource用來取代DriverManager來獲取Connection;經過DataSource得到Connection 速度快;經過DataSource得到Connection都是已經被包裹過的(不是驅動原來的鏈接),它的close方法已經被修改了;通常 DataSource內部會用一個鏈接池來緩存Connection,這樣能夠大幅度提升數據庫的訪問速度;鏈接池能夠理解成一個可以存放 Connection的Collection;咱們的程序只和DataSource打交道,不會直接訪問鏈接池.
(5).使用dbcp須要的三個包:common-dbcp.jar,common-collections.jar,common-pool.jar
17.使用JDBCTemplate工具類簡化對象查詢
(1).Spring框架中提供了一個JdbcTemplate工具類,JdbcTemplate類對JDBC API進行了很好的封裝,這個類就像咱們本身對JDBC進行封裝同樣,只是代碼更健壯和功能更強大而已,咱們之後在實際項目中能夠使用 JdbcTemplate類來徹底替換直接使用JDBC API,這與直接使用JDBC API沒有太大的性能區別,使用JdbcTemplate類須要額外從spring開發包中導入spring.jar和commons- logging.jar包
(2).JdbcTemplate的設計思想和前面的MyDaoTemplate類是相同的
static User findUser(String name){
JdbcTemplate jdbc = newJdbcTemplate(JdbcUtils.getDataSource());//參數是拿到一個數據源
String sql = "select id,name from userwhere name=?";
Object[]args=new Object[]{name};
jdbc.queryForObject(sql,args,newRowMapper(){//行映射器
public Object mapRow(ResultSet rs,introwNum){
User user = new User();
user.setId(rs.getInt("id"));
return user;
}
});
return null;
}
//這裏能夠不須要實現行映射器,能夠用類代替:
new BeanPropertyRowMapper(User.class);便可
18.使用JdbcTemplate實現Dao和用工廠模式靈活切換實現
(1).
public class UserDaoSpringImpl implementsUserDao{
private SimpleJdbcTemplatesimpleJdbcTemplate
= new SimpleJdbcTemplate(JdbcUtils.getDataSource());
//增長用戶
public void addUser(User user){
String sql = "insert into user(name,money,birthday)values(:name,:money,:birthday);
KeyHolder keyHolder = newGeneratedKeyHolder();
SqlParameterSource param = new BeanPropertySqlParameterSource(user);
this.simpleJdbcTemplate.getNamedParameterJdbcOperations().update(sql,param,keyHoler);
user.setId(keyHolder.getKey().intValue());
}
}
//增長user的代碼減小了太多了.
delete,update等方法都是大同小異
//刪除用戶
public void delete(User user){
String sql = "delete from user whereid=?";
this.simpleJdbcTemplate.update(sql,user.getId());
//使用了可變參數的特性,不須要複雜的Map存儲參數
}
//查詢用戶
public User findUser(StringloginName,String password){
//如何簡化返回查找集
String sql = "selectid,name,money,birthday from user where name=?";
returnthis.simpleJdbcTemplate.queryForObject(sql,ParameterizedBeanProertyRowMapper.newInstance(User.class),userId);
}
//更新用戶
public void update(User user){//如何替換佔位符?
String sql ="update user setname=?,birthday=?,money=? where id=?";
this.simpleJdbcTemplate.update(sql,user.getName(),user.getBirthday(),user.getMoney(),user.getId());
//這裏也能夠使用bean屬性的參數源;
}
代碼量大大減小了.
19.使用JDBC的批處理功能
(1).和數據庫打交道的成本是很高的,當須要發送多條sql語句時,成本更高了,這時就須要使用批處理技術,將多條查詢語句打成一個包.
(2).
for(int i=0;i<10000;i++){
ps.setString();
ps.setName();
ps.addBatch();//把一條更新語句增長到包中
}
int[] a = ps.executeBatch();//執行批處理,不是ps.executeUpdate();
(3).首先將語句打包時,並非包越大越好,若是包過大的話,可能形成內存溢出,因此可能將一個打包在分紅幾個小包進行發送,不一樣的數據庫,包的最適合大小是不一樣的.
(4).並非全部的批處理都能提升性能,這和不一樣的數據庫以及數據庫驅動決定的.
(5).Hibernate就是用了批處理技術,可是它進行了一些優化技術.
20.使用JDBC調用的存儲過程
(1).存儲過程常常用在之前的兩層結構中,如今的三層結構已經就用不到了
(2).CallableStatement(從PreparedStatement繼承來的)
java代碼:
CallableStatement cs=null;
String sql="{calladdUser(?,?,?,?)}";
cs=conn.prepareCall(sql);
//替換參數
cs.registerOutParameter(4,Types.INTEGER);
cs.setString(1,"ps name");
cs.setDate(2.new java.sql.Date(System.currentTimeMills()));
cs.setFloat(3,100f);
cs.executeUpdate();
int id = cs.getInt(4);
System.out.println(id);
存儲過程:
create procedure 'jdbc'.'addUser' (in pnamevarchar(45),in birthday date,in money float,out pid int)
//in:輸入參數,out:輸出參數
begin
insert intouser(name,birthday,money)values(pname,birthday,money);
select last_insert_id() into pid;
//last_insert_id()是一個函數,最後一次插入的id號
end $$
21.使用SimplejdbcTemplate和泛型技術簡化代碼
(1).
public class SimpleJdbcTemplateTest{
static SimpleJdbcTemplate simple = newSimpleJdbcTemplate(JdbcUtils.getDataSource());
static
T find(String nameClass
clazz){
String sql = "selectid,name,money,birthday from user where name=? and money=?";
User user =
simple.queryForObject(sql,ParameterizedBeanPropertyRowMapper.newInstance(User.class),name,100f);
}//使用了可變參數功能,沒有使用了參數數組,參數Map;使用泛型,將查詢的類型也當作是參數傳遞過來.
}
(2).它的內部也是包裝了NamedParameterJdbcOperations類,當將對象可變參數變成數組後,剩下的工做都交給NamedParameterJdbcOperations類作.
simple.getNamedParameterJdbcOperations()獲取NamedParameterJdbcOperations對象
simple.getJdbcOperations()獲取JdbcTemplate對象
22.使用策略模式對模板方法設計模式進行改進
(1).對於不一樣的查詢語句,返回的結果集可能不一樣,只要一個name,可是把全部的信息都查詢出來了,這就要求不一樣的映射結果.
public StringfindUserName(int id){
Stringsql="select name from user where id=?";
Object[]args=newObject[]{id};
}
protected ObjectrowMapper(ResultSet rs){//重新覆蓋rowMapper方法
returnrs.getString("name");
}
這種方式可能致使有多少條不一樣的查詢語句,就須要覆蓋多少次rowMapper方法.
(2).java中是不容許傳遞方法的,可是能夠傳遞一個類,接口
根據不一樣的sql中的內容,查詢的列不一樣,如:
select name fromuser:能夠獲得name一列
select id,namefrom user:能夠獲得id,name這兩列
selectid,name,money from user:能夠獲得id,name,money這三列.
public classMyDaoTemplate{
public Objectfind(String sql,Object[]args,RowMapper rowMapper){
obj =rowMapper.mapRow(rs);//映射的過程由一個接口去作
}
}
public interfaceRowMapper{//定義一個行映射器接口
public ObjectmapRow(ResultSet rs);
}
public classUserDaoImpl2{
MyDaoTemplate template= new MyDaoTemplate();
public UserfindUser(String loginName,String password){
Stringsql="select id,name,money,birthday from user where name=?";
Object[] args =new Object[]{loginName};
Object user =this.template.find(sql,args,new UserRowMapper());
retrun (User)user;
}
}
classUserRowMapper implements RowMapper{
public ObjectmapRow(ResultSet rs){//行映射器
User user = newUser();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setMoney(rs.getFloat("money"));
return user;
}
}
//當須要不一樣的查詢結果集,只需實現RowMapper接口就好了(能夠使用匿名內部方式實現)
(3).這是一種策略模式,根據不一樣的功能,調用不一樣的方法(策略),實現類組合的方式(在UserDaoImpl2類中定義一個MyDaoTemplate類)實現的,模板模式是根據繼承的方式實現的.
23.使用模板方法設計模式處理DAO中的查詢方法
publc abstractclass AbstractDao{
public Object find(String sql,Object[]args){//相同的部分在父類中實現
Connectionconn=null;
PreparedStatementps=null;
ResultSet rs=null;
conn=JdbcUtils.getConnection();
ps=conn.prepareStatement(sql);
for(inti=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
rs=ps.executQuery();
Object obj-null;
while(rs.next()){
obj=rowMapper(rs);
}
return obj;
}
abstract protectedObject rowMapper(ResultSet rs);//父類中不知道具體的查詢結果集.放到子類實現該方法.
}
public classUserDaoImpl extends AbstractDao{
public UserfindUser(String loginName,String password){//不變的部分放到子類實現.
Stringsql="select id,name,money,birthday from user where name=?";
Object[] args =new Object[]{loginName};
Object user = super.find(sql,args);
return (User)user;
}
@Override
protected ObjectrowMapper(ResultSet rs){//在子類中知道查詢結果有幾列
User user=newUser();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setMoney(rs.getFloat("money"));
user.setBirthday(rs.getDate("birthday"));
return user;
}
}
假設如今有一個帳戶AccountDao
public classAccountDaoImpl extends AbstractDao{
public UserfindAccount(int id){//不變的部分放到子類實現.
Stringsql="select id,name,money from account where id=?";
Object[] args =new Object[]{id};
Object user =super.find(sql,args);
return(Account)account;
}
@Override
protected ObjectrowMapper(ResultSet rs){//在子類中知道查詢結果有幾列
Accountaccount=new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getFloat("money"));
return account;
}
}
public classAccount{
private int id;
private Stringname;
private floatmoney;
//get/set方法省略
}
模板模式,相同的步驟放到父類中,不一樣的步驟放到子類中設計,service方法,doGet(),doPost()方法,首先調用service方法.service會根據method參數的值來調用doGet(),doPost()方法.
24.使用支持命名參數的JdbcTemplate
(1).Spring的NamedParameterJdbcTemplate
第一:NamedParameterJdbcTemplate內部包含了一個JdbcTemplate,因此JdbcTemplate能作的事情 NamedParameterJdbcTemplate都能幹,NamedParameterJdbcTemplate相對於JdbcTemplate主 要增長了參數能夠命名的功能
第二:public Object queryForObject(String sql,MapparamMap,RowMapper rowMapper)
第三:public Object queryForObject(Stringsql,SqlParameterSoruce paramSource,RowMapper rowMapper)
SqlParameterSource的兩個主要實現MapSqlParameterSource和BeanPropertySqlParameterSource
第四:public int update(String sql,SqlParameterSourceparamSource,KeyHolder generatedKeyHolder)保存數據得到主鍵
(2).在傳遞參數時,須要將參數Object[]args與?佔位符的位置對應好,若是對應錯了,就會出現問題,這時,咱們就能夠給佔位符起個別名
staticNamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate();
Stringsql="select id from user where name=:n and money>:m andid<:id";
Map params=newHashMap();//使用Map存放參數,而不是數組了
params.put("n",user.getName());
params.put("m",user.getMoney());
params.put("id",user.getId());
/Object[]args=new Object[]{user.getName(),user.getMoney(),user.getId()};/
Object u =named.queryForObject(sql,params,new BeanPropertyRowMapper(),User.class));
//注意sql的書寫,將佔位符?替換了,注意替換的規則.NamedParameterJdbcTemplate只幹了一件事,就是將佔位符?替換成變量名,將參數命名話後,以後的操做都會交給JdbcTemplate處理.
(3).爲何要使用命名參數:
SqlParameterSourceps = new BeanPropertySqlParameterSource(user);//關於user的bean參數源
Stringsql="select id from user where name=:name and money>:money andid<:id";
Object u =named.queryForObject(sql,ps,new BeanPropertyRowMapper(),User.class));
這時參數就存放在user參數源中,參數名必須和user的屬性名同樣,將參數封裝成一個類(參數源),符合面向對象設計思想
(4).保存數據,拿到記錄的主鍵.當主鍵是符合類型(就是多列組成),也多是String類型的.
static voidaddUser(User user){
String sql ="insert into user(name,birthday,money) value(:name,:birthday,:money);
SqlParameterSourceps = new BeanPropertySqlParameterSource(user);//關於user的bean參數源
KeyHolderkeyHolder = new GeneratedKeyHolder();
named.update(sql,ps,keyHolder);
//插入的記錄的主鍵放到keyHoler中
int id =keyHolder.getKey().inValue();
user.setId(id);
Map map =keyHolder.getKeys();//主鍵由多列組成的時候
}//重點
25.事務的保存點處理
(1).當事務進行回滾時,不是所有進行回滾,有時只想回滾一部分的操做,
(2).Savepoint sp=null;
sp=conn.setSavepoint();//設置保存點
if(conn!=null&&sp!=null){
conn.rollback(sp);//將保存點當作參數,只回滾到保存點
}
26.事務的概念與JDBC事務處理
(1).事務的特性:(ACID)
原子性(atomicity):組成事務處理的語句造成了一個邏輯單元,不能只執行其中的一部分
一致性(consistency):在事務處理執行先後,數據庫是一致的(數據庫數據完整性約束)
隔離性(isolcation):一個事務處理對另外一個事務處理的影響持久性(durability):事務處理的效果可以被永久保存下來
(2).connection.setAutoCommit(false)//打開事務
connection.commit();//提交事務
connection.rollback();//回滾事務
(3).查看數據庫表的引擎是否支持事務的操做
27.事務的隔離級別
(1).當兩個事務同時去操做同一個數據源,這就是隔離性
(2).設置隔離級別:connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
隔離級別:
讀未提交:可能出現髒讀,不可重複度,幻度
讀已提交:不可能髒讀,可能出現不可重複讀,幻讀
可重複讀:不可能髒讀,不可重複讀,可能出現幻讀
可串行化:不可能髒讀,不可重複讀,幻讀
級別逐漸提升.當級別越高的時候,須要的資源越多,對併發的操做有影響.
髒讀:別人的數據沒有提交,我就讀到了.
不可重複讀:第一次讀和第二次讀的結果不一樣
幻讀:當咱們正在查詢id>10的記錄,這時有另一個事務增長一條正好id>10的記錄.
(3).隔離級別的缺省值和數據庫相關.不一樣數據庫擁有的隔離級別的個數也是不一樣的.
28.數據庫的元數據信息
(1).能夠獲得數據庫的相關信息,是否支持事務,數據庫的名稱,版本號,隔離級別等信息.
Connectionconn=JdbcUtils.getConnection();
DatabaseMetaDatadbmd =conn.getMetaData()//返回數據的元信息
dbmd.getDatabaseProductName();//獲得數據庫的名稱
(2).hibernate支持各類數據庫,因此它確定須要知道全部數據庫的相關信息.
29.經過代理模式來保持用戶關閉鏈接的習慣
(1).用戶可能不使用JdbcUtils.free()方法釋放鏈接,而是按照conn.close()方法釋放鏈接,這時咱們建立的鏈接池就沒有用了,鏈接數也就減小了,因此咱們但願用戶始終使用咱們本身編寫的方法進行釋放鏈接
(2).經過close()方法,仍是可以將鏈接方法鏈接池中,因此咱們要攔截close()方法,組合優先繼承
(3).public classMyConnection implements Connection{
private ConnectionrealConnection;//使用組合方式
privateMyDataSource dataSource;
MyConnection(ConnectionrealConnection,MyDataSource dataSource){
this.realConnection=connection;
this.dataSource=dataSource;
}
//實現Connection的全部方法
public voidclose(){//這裏就能夠實現Connection的close()方法了
this.dataSource.connectionPool.addLast(this);//把本身從新放到池中.
}
(4).此時代碼中的Connection處都使用MyConnection,這就是面向接口編程的好處.同時類MyConnection的訪問權限是包訪問權限,不許用戶訪問的,可是容許在dataSource中訪問.
(5).DataSource類和MyConnection類之間相互調用.
(6).MyConnection是個代理,是Connection的代理模式,實現Connection的close()方法.這就是靜態代理設計模式,在MyConnection類中定義一個Connection,這是組合方式,也能夠使用集成方式實現代理.
30.完成數據庫的CRUD操做
(1).書寫SQL語句時應該注意的問題:select * from user,就是不該該寫星號,最好書寫列名,獲得數據,能夠根據列的索引號,也能夠根據列名,建議使用根據列名取數據.
31.用jdbc訪問大段文本數據
(1).數據庫中的varchar最大是255個字節,因此就須要使用大文本類型TEXT.只有純文本格式才能放進去.
(2).
Stringsql="insert into clob_test(big_text) value(?)";
ps=conn.prepareState(sql);
File file=newFile("src/cn/itcast/jdbc/JdbcUtils.java");
Reader reader =new BufferedReader(new FileReader(file));//可能含有IO的異常
ps.setAsciiStream(1,reader,(int)file.length());//須要一個Reader,字符流的長度Length,這個方法只能用於文本只含有Ascii碼的
inti=ps.executeUpdate(sql);
reader.close();
rs=st.executeQuery("selectbig_text from clob_test");//讀取文本類型數據
while(rs.net()){
Clob clob =rs.getClob(1);
Reader reader =clob.getCharacterStream();
File file=newFile("JdbUtils_bak.java");
Writer writer=newBufferedWriter(new FileWriter(file));
char[]buff=newchar[1024];
for(inti=0;(i=reader.read(buff))>0;){
writer.write(buff,0,i);
}
}
writer.close();
reader.close();
7、 iBaits
優勢:
ibatis把sql語句從Java源程序中獨立出來,放在單獨的XML文件中編寫,給程序的維護帶來了很大便利。
ibatis封裝了底層JDBC API的調用細節,並能自動將結果集轉換成Java Bean對象,大大簡化了Java數據庫編程的重複工做。
簡單易於學習,易於使用,很是實用。
由於Ibatis須要程序員本身去編寫sql語句,程序員能夠結合數據庫自身的特色靈活控制sql語句,所以可以實現比hibernate等全自動orm框架更高的查詢效率,可以完成複雜查詢。
阿里巴巴、慧點科技等多家知名軟件公司都使用Ibatis。
缺點:
1.CRUD的操做只能帶一個參數
2.和Hibernate相比,須要編寫Sql語句,可是Hibernate不須要編寫Sql語句