hibernate和jdbc总结

hibernate:

1.hibernate的工作原理.

原理:
1.通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件
2.由hibernate.cfg.xml中的<mapping resource="com/xx/User.hbm.xml"/>读取并解析映射信息
3.通过config.buildSessionFactory();//创建SessionFactory
4.sessionFactory.openSession();//打开Sesssion
5.session.beginTransaction();//创建事务Transation
6.persistent operate持久化操作
7.session.getTransaction().commit();//提交事务
8.关闭Session
9.关闭SesstionFactory

 

 

2.说说hibernate的缓存.

1、一级缓存

可以简单的理解为session缓存,即同一个session中使用get、load、迭代,会优先重缓存中取,如果没有则查询数据库并存入缓存,session关闭后缓存清空

 2、二级缓存

可以直接理解为升级版,即sessionFactory 缓存,即只要使用的session是同一个sessionFactory创建的则可共用缓存,并且修改、删除、添加、会更新缓存。

3、查询缓存

也就是三级缓存(说三级缓存的很坑),我的理解就是query缓存,使用query.setCacheable(true) 即可开启查询缓存,查询缓存只缓存普通属性结果集,不缓存整个实体类结果集,以及实体类ID结果集,查询缓存是针对普通属性结果集的缓存对实体对象的结果集只缓存id,查询缓存在查询实体类时会首先到查询缓存中查询ID然后根据ID到一级或者二级缓存中查询实体类,没有则从数据库取,查询普通属性时也是优先从缓存中取。

  关闭二级缓存,开启查询缓存,调用query.list()会发出N条语句,因为查询缓存是根据ID到二级缓存中找的,没有则查找数据库,二级缓存和查询缓存同时开启则查询同样SQL语句时不会发出数据库请求,而是直接从缓存中取。

 备注:

查询缓存只会query.list()起作用.

session.find方法永远是从数据库中获取数据

缓存通过map 使用ID作为KEY来缓存对象、一级和二级缓存只缓存对象

 

 

 

3.hibernate的检索方式有哪些.

Hibernate检索数据的五种方式:

1.导航对象图检索方式。(根据已经加载的对象,导航到其他对象。)

2.OID检索方式。(按照对象的OID来检索对象。)

3.HQL检索方式。(使用面向对象的HQL查询语言。)

4.QBC检索方式。(使用QBC(Qurey By Criteria) API来检索对象。)

5.本地SQL检索方式。(使用本地数据库的SQL查询语句。)

 

1、导航对象图检索方式

利用类与类之间的关系来检索对象。譬如我们要查找一份订单,就可以由订单对象自动导航找到订单所属的客户对象。当然,前提是必须在对象-关系映射文件上配置了它们的多对一的关系。

Order order = (Order)session.get(Order.class,1);

Customer customer = order.getCustomer();

 

2、OID检索方式

主要指用Session的get()和load()方法加载某条记录对应的对象。

Customer customer = (Customer)session.get(Customer.class,1);

Customer customer = (Customer)session.load(Customer.class,1);

 

3、HQL检索方式

HQL(hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似。在Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。它具有以下功能:

在查询语句中设定各种查询条件。

支持投影查询,即仅检索出对象的部分属性。

支持分页查询。

支持分组查询,允许使用group by和having关键字。

提供内置聚集函数,如sum()、min()和max()。

能够调用用户定义的SQL函数。

支持子查询,即嵌套查询。

支持动态绑定参数。

 

Session类的Qurey接口支持HQL检索方式,它提供了以上列出的各种查询功能。

注:Qurey接口支持方法链编程风格,它的set方法都返回自身实例,而不是返回void类型。方法链编程风格能使程序代码更加简洁。

示例代码:

Query query = session.createQuery("from Customeras c where " +"c.name=:customerName andc.age=:customerAge");

// 动态绑定参数 query.setString("customerName", "Test"); query.setInteger("customerAge", 21);

// 执行检索 Listresult = query.list();

// 方法链编程风格 Listresult1 = session.createQuery( "from Customer as c wherec.name=:customerName and c.age=:customerAge").setString("customerName", "Test").setInteger("customerAge",21) .list();

 

4、QBC(Qurey ByCriteria)检索方式

采用HQL检索方式时,在应用程序中需要定义基于字符串形式的HQL查询语句。QBC API提供了检索对象的另一种方式,它主要由Criteria接口、Criterion接口和Expression类组成,它支持在运行时动态生成查询语句。

示例代码:

Criteria criteria =session.createCriteria(Customer.class);

Criterion criterion1 = Expression.like("namr","T%");

Criterion criterion2 = Expression.eq("age", newInteger(21));

criteria = criteria.add(criterion1);

criteria = criteria.add(criterion2);

// 执行检索 Listresult = criteria.list();

// 方法链编程风格 Listresult1 =session.createCriteria(Customer.class).add(Expression.like("namr""T%")).add(Expression.

eq("age", new Integer(21))).list();

Hibernate还提供了QBE(Qurey By Example)检索方式,它是QBC的子功能。QBE允许先创建一个随想模板,然后检索出和这个样板相同的对象。

示例代码:

Customer exampleCustomer=new Customer();

exampleCustomer.setAge(21);

List result1 = session.createCriteria(Customer.class).add(Example.create(exampleCustomer)).list();

QBE的功能不是特别强大,仅在某些场合下有用。一个典型的使用场合就是在查询窗口中让用户输入一系列的查询条件,然后返回匹配的对象。QBE方式目前只能支持对象属性字段的等于查询和字符串的模糊匹配,不能支持区间,或者,大于等操作。在这些情况下,还是采用HQL检索方式或QBC检索方式。

 

5、本地SQL检索方式

采用HQL或QBC检索方式时,Hibernate生成标准的SQL查询语句,使用于所有的数据库平台,因此这两种检索方式都是跨平台的。有的应用程序可能需要根据底层数据库的SQL方言,来生成一些特殊的查询语句。在这种情况下,可以利用Hibernate提供的SQL检索方式。

示例代码:

Query query = session.createSQLQuery("select {c.*} from CUSTOMER as cwhere c.NAME like :customerName andc.AGE=:customerAge");

// 动态绑定参数 query.setString("customerName", "Test"); query.setInteger("customerAge", 21);

// 执行检索 List result = query.list();

 

以上我们看到了五种检索方式的应用。

 

 

 

4.hibernate主键生成的策略有哪些, 各有什么特色.

1、assigned

主键由外部程序负责生成,在 save() 之前必须指定一个。Hibernate不负责维护主键生成。与Hibernate和底层数据库都无关,可以跨数据库。在存储对象前,必须要使用主键的setter方法给主键赋值,至于这个值怎么生成,完全由自己决定,这种方法应该尽量避免。

<id name="id" column="id">

<generator class="assigned" />

</id>

“ud”是自定义的策略名,人为起的名字,后面均用“ud”表示。

特点:可以跨数据库,人为控制主键生成,应尽量避免。

2、increment

由Hibernate从数据库中取出主键的最大值(每个session只取1次),以该值为基础,每次增量为1,在内存中生成主键,不依赖于底层的数据库,因此可以跨数据库。

<id name="id" column="id">

<generator class="increment" />

</id>

Hibernate调用org.hibernate.id.IncrementGenerator类里面的generate()方法,使用select max(idColumnName) from tableName语句获取主键最大值。该方法被声明成了synchronized,所以在一个独立的Java虚拟机内部是没有问题的,然而,在多个JVM同时并发访问数据库select max时就可能取出相同的值,再insert就会发生Dumplicate entry的错误。所以只能有一个Hibernate应用进程访问数据库,否则就可能产生主键冲突,所以不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。

官方文档:只有在没有其他进程往同一张表中插入数据时才能使用,在集群下不要使用。

特点:跨数据库,不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。

3、hilo

hilo(高低位方式high low)是hibernate中最常用的一种生成方式,需要一张额外的表保存hi的值。保存hi值的表至少有一条记录(只与第一条记录有关),否则会出现错误。可以跨数据库。

<id name="id" column="id">

<generator class="hilo">

<param name="table">hibernate_hilo</param>

<param name="column">next_hi</param>

<param name="max_lo">100</param>

</generator>

</id>

<param name="table">hibernate_hilo</param> 指定保存hi值的表名

<param name="column">next_hi</param> 指定保存hi值的列名

<param name="max_lo">100</param> 指定低位的最大值

也可以省略table和column配置,其默认的表为hibernate_unique_key,列为next_hi

<id name="id" column="id">

<generator class="hilo">

<param name="max_lo">100</param>

</generator>

</id>

hilo生成器生成主键的过程(以hibernate_unique_key表,next_hi列为例):

1. 获得hi值:读取并记录数据库的hibernate_unique_key表中next_hi字段的值,数据库中此字段值加1保存。

2. 获得lo值:从0到max_lo循环取值,差值为1,当值为max_lo值时,重新获取hi值,然后lo值继续从0到max_lo循环。

3. 根据公式 hi * (max_lo + 1) + lo计算生成主键值。

注意:当hi值是0的时候,那么第一个值不是0*(max_lo+1)+0=0,而是lo跳过0从1开始,直接是1、2、3……

那max_lo配置多大合适呢?

这要根据具体情况而定,如果系统一般不重启,而且需要用此表建立大量的主键,可以吧max_lo配置大一点,这样可以减少读取数据表的次数,提高效率;反之,如果服务器经常重启,可以吧max_lo配置小一点,可以避免每次重启主键之间的间隔太大,造成主键值主键不连贯。

特点:跨数据库,hilo算法生成的标志只能在一个数据库中保证唯一。

4、seqhilo

与hilo类似,通过hi/lo算法实现的主键生成机制,只是将hilo中的数据表换成了序列sequence,需要数据库中先创建sequence,适用于支持sequence的数据库,如Oracle。

<id name="id" column="id">

<generator class="seqhilo">

<param name="sequence">hibernate_seq</param>

<param name="max_lo">100</param>

</generator>

</id>

 

特点:与hilo类似,只能在支持序列的数据库中使用。

5、sequence

采用数据库提供的sequence机制生成主键,需要数据库支持sequence。如oralce、DB、SAP DB、PostgerSQL、McKoi中的sequence。MySQL这种不支持sequence的数据库则不行(可以使用identity)。

<generator class="sequence">

<param name="sequence">hibernate_id</param>

</generator>

<param name="sequence">hibernate_id</param> 指定sequence的名称

Hibernate生成主键时,查找sequence并赋给主键值,主键值由数据库生成,Hibernate不负责维护,使用时必须先创建一个sequence,如果不指定sequence名称,则使用Hibernate默认的sequence,名称为hibernate_sequence,前提要在数据库中创建该sequence。

特点:只能在支持序列的数据库中使用,如Oracle。

6、identity

identity由底层数据库生成标识符。identity是由数据库自己生成的,但这个主键必须设置为自增长,使用identity的前提条件是底层数据库支持自动增长字段类型,如DB2、SQL Server、MySQL、Sybase和HypersonicSQL等,Oracle这类没有自增字段的则不支持。

<id name="id" column="id">

<generator class="identity" />

</id>

例:如果使用MySQL数据库,则主键字段必须设置成auto_increment。

id int(11) primary key auto_increment

特点:只能用在支持自动增长的字段数据库中使用,如MySQL。

7、native

native由hibernate根据使用的数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式,灵活性很强。如果能支持identity则使用identity,如果支持sequence则使用sequence。

<id name="id" column="id">

<generator class="native" />

</id>

例如MySQL使用identity,Oracle使用sequence

注意:如果Hibernate自动选择sequence或者hilo,则所有的表的主键都会从Hibernate默认的sequence或hilo表中取。并且,有的数据库对于默认情况主键生成测试的支持,效率并不是很高。

使用sequence或hilo时,可以加入参数,指定sequence名称或hi值表名称等,如

<param name="sequence">hibernate_id</param>

特点:根据数据库自动选择,项目中如果用到多个数据库时,可以使用这种方式,使用时需要设置表的自增字段或建立序列,建立表等。

8、uuid

UUID:Universally Unique Identifier,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字,标准的UUID格式为:

xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12)

其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。

<id name="id" column="id">

<generator class="uuid" />

</id>

Hibernate在保存对象时,生成一个UUID字符串作为主键,保证了唯一性,但其并无任何业务逻辑意义,只能作为主键,唯一缺点长度较大,32位(Hibernate将UUID中间的“-”删除了)的字符串,占用存储空间大,但是有两个很重要的优点,Hibernate在维护主键时,不用去数据库查询,从而提高效率,而且它是跨数据库的,以后切换数据库极其方便。

特点:uuid长度大,占用空间大,跨数据库,不用访问数据库就生成主键值,所以效率高且能保证唯一性,移植非常方便,推荐使用。

9、guid

GUID:Globally Unique Identifier全球唯一标识符,也称作 UUID,是一个128位长的数字,用16进制表示。算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成GUID。从理论上讲,如果一台机器每秒产生10000000个GUID,则可以保证(概率意义上)3240年不重复。

<id name="id" column="id">

<generator class="guid" />

</id>

Hibernate在维护主键时,先查询数据库,获得一个uuid字符串,该字符串就是主键值,该值唯一,缺点长度较大,支持数据库有限,优点同uuid,跨数据库,但是仍然需要访问数据库。

注意:长度因数据库不同而不同

MySQL中使用select uuid()语句获得的为36位(包含标准格式的“-”)

Oracle中,使用select rawtohex(sys_guid()) from dual语句获得的为32位(不包含“-”) 

特点:需要数据库支持查询uuid,生成时需要查询数据库,效率没有uuid高,推荐使用uuid。

10、foreign

使用另外一个相关联的对象的主键作为该对象主键。主要用于一对一关系中。

<id name="id" column="id">

<generator class="foreign">

<param name="property">user</param>

</generator>

</id>

<one-to-one name="user" class="domain.User" constrained="true" />

该例使用domain.User的主键作为本类映射的主键。

特点:很少使用,大多用在一对一关系中。

11、select

使用触发器生成主键,主要用于早期的数据库主键生成机制,能用到的地方非常少。

12、其他注释方式配置

注释方式与配置文件底层实现方式相同,只是配置的方式换成了注释方式

自动增长,适用于支持自增字段的数据库

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

根据底层数据库自动选择方式,需要底层数据库的设置

如MySQL,会使用自增字段,需要将主键设置成auto_increment。

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

使用表存储生成的主键,可以跨数据库。

每次需要主键值时,查询名为"hibernate_table"的表,查找主键列"gen_pk"值为"2"记录,得到这条记录的"gen_val"值,根据这个值,和allocationSize的值生成主键值。

@Id

@GeneratedValue(strategy = GenerationType.TABLE, generator = "ud")

@TableGenerator(name = "ud",

table = "hibernate_table",

pkColumnName = "gen_pk",

pkColumnValue = "2",

valueColumnName = "gen_val",

initialValue = 2,

allocationSize = 5)

使用序列存储主键值

@Id

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ud")

@SequenceGenerator(name = "ud",

sequenceName = "hibernate_seq",

allocationSize = 1,

initialValue = 2)

13、小结

1、为了保证对象标识符的唯一性与不可变性,应该让Hibernate来为主键赋值,而不是程序。

2、正常使用Hibernate维护主键,最好将主键的setter方法设置成private,从而避免人为或程序修改主键,而使用assigned方式,就不能用private,否则无法给主键赋值。

2、Hibernate中唯一一种最简单通用的主键生成器就是uuid。虽然是个32位难读的长字符串,但是它没有跨数据库的问题,将来切换数据库极其简单方便,推荐使用!

3、自动增长字段类型与序列

数据库

自动增长字段

序列

MySQL

 

Oracle

 

DB2

MS SQL Server

 

Sybase

 

HypersonicSQL

 

PostgreSQL

 

SAP DB

 

HSQLDB

 

Infomix

 

4、关于hilo机制注意:

hilo算法生成的标志只能在一个数据库中保证唯一。

当用户为Hibernate自行提供连接,或者Hibernate通过JTA,从应用服务器的数据源获取数据库连接时,无法使用hilo,因为这不能保证hilo单独在新的数据库连接的事务中访问hi值表,这种情况,如果数据库支持序列,可以使用seqhilo。

5、使用identity、native、GenerationType.AUTO等方式生成主键时,只要用到自增字段,数据库表的字段必须设置成自动增加的,否则出错。

6、还有一些方法未列出来,例如uuid.hex,sequence-identity等,这些方法不是很常用,且已被其他方法代替,如uuid.hex,官方文档里建议不使用,而直接使用uuid方法。

7、Hibernate的各版本主键生成策略配置有略微差别,但实现基本相同。如,有的版本默认sequence不指定序列名,则使用名为hibernate_sequence的序列,有的版本则必须指定序列名。

8、还可以自定义主键生成策略,这里暂时不讨论,只讨论官方自带生成策略。 

 

 

5.load()和get()的区别是什么.

(1)Hibernate的get方法,会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查询数据库,数据库中没有就返回null。

(2)Hibernate的load方法加载实体对象的时候,根据映射文件上类级别的lazy属性的配置(默认为true):
①若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加载,返回实体的代理类对象(该代理类为实体类的子类,由CGLIB动态生成)。等到具体使用该对象(除获取OID以外)的时候,再查询二级缓存和数据库,若仍没发现符合条件的记录,则会抛出一个ObjectNotFoundException。 
②若为false,就跟Hibernate的get方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException,所说的load方法抛异常是指在使用该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时。

 

 

6.hibenate的二级缓存的特点.

二级缓存:

         Hibernate提供了基于应用程序级别的缓存, 可以跨多个session,即不同的session都可以访问缓存数据。 这个缓存也叫二级缓存。

         Hibernate提供的二级缓存有默认的实现,且是一种可插配的缓存框架!如果用户想用二级缓存,只需要在hibernate.cfg.xml中配置即可;不想用,直接移除,不影响代码。

         如果用户觉得hibernate提供的缓存框架不好用,自己可以换其他的缓存框架或自己实现缓存框架都可以。

 

        Hibernate的二级缓存的实现原理与一级缓存是一样的,也是通过以ID为key的Map来实现对对象的缓存。

        由于Hibernate的二级缓存是作用在SessionFactory范围内的,因而它比一级缓存的范围更广,可以被所有的Session对象所共享。

 

 

7.谈谈hibernate的缓存。

一 Hibernate的一级缓存(session内置缓存)。
Hibernate的一级缓存是Hibernate内置的缓存,意思是必须使用的,不可以卸载的。Hibernate的一级缓存实际上就是session级别的缓存,其实现方式是在session的生命周期内缓存访问过的对象,通过key-value这样的数据结构保存在内存中,比如每个session,对同一个id, load两次,只会发送一条sql语句到数据库,第二次直接从一级缓存中找。由于一级缓存只是在session周期范围内有效,因为效果不是很理想。
二 Hibernate的二级缓存(包括sessionFactory内置缓存和外置缓存)
Hibernate的二级缓存,是sessionFactory级别的缓存,也就是进程级别的缓存,跟应用的生命周期一致,Hibernate的二级缓存是可选的缓存配置,缺省状态下,sessionFactory外置缓存是没有开启的,如果需要配置,则用户需要自己进行相关配置。
Hibernate的二级缓存包括内置缓存和外置缓存。
1.内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是来源于映射文件中数据,而预定义SQL语句是在Hibernate初始化阶段根据映射元数据推导出来,SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。这就是为什么我们修改了hibernate的映射配置文件后,必须重新启动应用系统的原因。
2.外置缓存是可拔插的缓存插件,也就是说支持第三方的缓存组件,如ehcache、oscache等,需要设置hibernate.cache.provider_class。 外置缓存里主要存放2方面的数据:class数据和查询缓存数据。当然查询缓存要开启才生效。下边分别说说。
a. class 缓存
Class缓存其实跟Hibernate的一级缓存实现上差不多,都是通过key-value的方式存放数据,key用一条记录的id,记录的数据作为value。跟一级缓存主要的区别是它们的生命周期不一样,一级缓存的生命周期短,只在session范围,也可以说是事务范围内有效。而这里的class缓存则在整个应用内有效。要使用class 缓存需要配置相应的参数,比如<cache name="com.xxx.cache.User" maxElementsInMemory="1000" eternal="false" timeToLiveSec timeToIdleSec overflowToDisk="true" />这些参数,一旦配置了class缓存,则无论是hibernate的load,get,list,literate方法,只要从数据库select出数据来,都要首先存放到class缓存中,以便下次访问使用。主要这里的配置参数:name表示缓存的类全名,maxElementsInMemory表示缓存在内存中的元素的最大个数。Eternal表示是否永久保存,timeToLiveSec表示对象存货周期。overflowToDisk如果配置为true,则缓存中元素超过maxElementsInMemory配置的个数时,就会把存放不下的数据从存放到硬盘的临时文件中去。
b.查询缓存
Hibernate的查询缓存的实现方式也是key-value这样的数据结构。Key是查询数据时使用的SQL的全部字符串,包括查询参数,分页等信息,如,select * from User u where u.name like ? 而value并不是返回的结果集,而是返回的结果集的id列表。下次查询同样的sql时,在缓存中找到这些id列表,然后去class缓存中,一个一个的load出对应的记录。这就看得出来,实际上查询缓存时依赖于class缓存的。
查询缓存一般都需要通过hibernate.cache.use_query_cache=true  这样的设置来开启,当然还需要其他参数的配置,这里不细说。

在使用hibernate的查询缓存和class缓存时,有以下经验分享:
1.由于查询缓存的使用依赖于class缓存,因此class缓存得超时时间设置的必须大于查询缓存的超时时间,也就是timeToLiveSec这个参数的设置。否则可能因为class缓存得失效导致查询缓存在通过id取对象时从缓存中找不到数据只能从数据库取,导致性能地下。
2.有时程序员因为解决某个问题,没有从全局考虑,把对象从缓存中evict了。可能导致一些其他问题。
3.关于查询缓存的失效机制:只要更新过一个表,那么涉及到这个表的任何查询缓存将失效。所以说hibernate的命中率是很低的,需要根据实际业务的读写比率考虑是否用查询缓存。
4.关于查询缓存失效机制的实现:对于每个查询缓存,hibernate记录了其生成时间,而对于每个表,都有一个更新时间,每次进行数据的更新操作时,都会更新这个更新时间。判断查询缓存失效的原则:只要查询缓存的key对应的sql语句中涉及的表中有一个表的更新时间大于这个缓存的生成时间,则该查询缓存失效。
5.使用hibernate二级缓存必须注意的问题:整个系统必须只通过hibernate写数据,也就是hibernate独占写操作。因为如果通过其他方式如JDBC,存储过程等更新数据后,hibernate的2级缓存并不知道数据更新了,其缓存也不会失效,因为会出现脏数据读取的问题。当然如果遇到有些应用实在必须要同时兼顾其他方式写数据的话,可以通过调用sessionFactory提供的一些清除缓存的方法来移除对应的缓存。但是这样增加了维护的复杂度。

 

 

 

8.hibernate和spring如何整合。

9、Hibernate中save、update和saveOrUpdate方法的不同之处?

(1)save:执行保存操作的,对一个新new出来的对象进行保存,数据库中没有这个对象。如果数据库中有,会报错说有重复的记录。
(2)update:如果是对一个已经存在的托管对象进行更新,要使用update()方法了,数据中有这个对象。
(3)saveOrUpdate:这个方法是更新或者插入,有主键就执行更新,如果没有主键就执行插入。是根据实体类对象的状态做的不同操作。
①实体类对象为临时态时做的添加save操作。
②实体类对象为游离态、持久态时做的修改update操作。

jdbc:

1.mysql和oracle的端口


MySQL 默认端口号为:3306

URL:jdbc:mysql://localhost:3306/test?user=root&password=&useUnicode=true&characterEncoding=gbk

DRIVERNAME:"com.mysql.jdbc.Driver";



Oracle 默认端口号为:1521

URL:"jdbc:oracle:thin:@localhost :1521:orcl";

DRIVERNAME:"oracle.jdbc.driver.OracleDriver";

 

 

2.mysql分页, oracle如何分页;

(1)MySql的Limit m,n语句

Limit后的两个参数中,参数m是起始下标,它从0开始;参数n是返回的记录数。我们需要分页的话指定这两个值即可

(2)Oracle数据库的rownum

在Oracle数据库中,分页方式没有MySql这样简单,它需要依靠rownum来实现.
Rownum表示一条记录的行号,值得注意的是它在获取每一行后才赋予.因此,想指定rownum的区间来取得分页数据在一层查询语句中是无法做到的,要分页还要进行一次查询.

SELECT * FROM 
(
SELECT A.*, ROWNUM RN 
FROM (SELECT * FROM TABLE_NAME) A 
WHERE ROWNUM <= 40
)
WHERE RN >= 21

其中最内层的查询SELECT * FROM TABLE_NAME表示不进行翻页的原始查询语句。ROWNUM <= 40和RN >= 21控制分页查询的每页的范围。

上面给出的这个分页查询语句,在大多数情况拥有较高的效率。分页的目的就是控制输出结果集大小,将结果尽快的返回。在上面的分页查询语句中,这种考虑主要体现在WHERE ROWNUM <= 40这句上。

选 择第21到40条记录存在两种方法,一种是上面例子中展示的在查询的第二层通过ROWNUM <= 40来控制最大值,在查询的最外层控制最小值。而另一种方式是去掉查询第二层的WHERE ROWNUM <= 40语句,在查询的最外层控制分页的最小值和最大值。这是,查询语句如下:

SELECT * FROM 
(
SELECT A.*, ROWNUM RN 
FROM (SELECT * FROM TABLE_NAME) A 
)
WHERE RN BETWEEN 21 AND 40

对比这两种写法,绝大多数的情况下,第一个查询的效率比第二个高得多。

这 是由于CBO优化模式下,Oracle可以将外层的查询条件推到内层查询中,以提高内层查询的执行效率。对于第一个查询语句,第二层的查询条件WHERE ROWNUM <= 40就可以被Oracle推入到内层查询中,这样Oracle查询的结果一旦超过了ROWNUM限制条件,就终止查询将结果返回了。

而 第二个查询语句,由于查询条件BETWEEN 21 AND 40是存在于查询的第三层,而Oracle无法将第三层的查询条件推到最内层(即使推到最内层也没有意义,因为最内层查询不知道RN代表什么)。因此,对 于第二个查询语句,Oracle最内层返回给中间层的是所有满足条件的数据,而中间层返回给最外层的也是所有数据。数据的过滤在最外层完成,显然这个效率 要比第一个查询低得多。

上面分析的查询不仅仅是针对单表的简单查询,对于最内层查询是复杂的多表联合查询或最内层查询包含排序的情况一样有效。

 

3.jdbc的执行步骤.

  1、加载JDBC驱动程序:      
在连接数据库之前,首先要加载想要连接的数据库的驱动到JVMJava虚拟机),
这通过java.lang.Class类的静态方法forName(String className)实现。
例如:
try{
//加载MySql的驱动类
Class.forName("com.mysql.jdbc.Driver") ;
}catch(ClassNotFoundException e){
System.out.println("找不到驱动程序类 ,加载驱动失败!");
e.printStackTrace() ;
}
成功加载后,会将Driver类的实例注册到DriverManager类中。
2、提供JDBC连接的URL
•连接URL定义了连接数据库时的协议、子协议、数据源标识。
•书写形式:协议:子协议:数据源标识
协议:在JDBC中总是以jdbc开始 子协议:是桥连接的驱动程序或是数据库管理系统名称。
数据源标识:标记找到数据库来源的地址与连接端口。
例如:
(MySql的连接URL
jdbc:mysql: //localhost:3306/test?useUnicode=true&characterEncoding=gbk ;
useUnicode=true:
表示使用Unicode字符集。如果characterEncoding设置为 gb2312或GBK,本参数必须设置为true 。characterEncoding=gbk:字符编码方式。
3、创建数据库的连接
•要连接数据库,需要向java.sql.DriverManager请求并获得Connection对象, 该对象就代表一个数据库的连接。
•使用DriverManager的getConnectin(String url , String username , String password )方法传入指定的欲连接的数据库的路径、数据库的用户名和 密码来获得。
例如: //连接MySql数据库,用户名和密码都是root
String url = "jdbc:mysql://localhost:3306/test" ;
String username = "root" ;
String password = "root" ;
try{
Connection con = DriverManager.getConnection(url , username , password ) ;
}catch(SQLException se){
System.out.println("数据库连接失败!");
se.printStackTrace() ;
}
4、创建一个Statement
•要执行SQL语句,必须获得java.sql.Statement实例,Statement实例分为以下3 种类型:
1、执行静态SQL语句。通常通过Statement实例实现。
2、执行动态SQL语句。通常通过PreparedStatement实例实现。
3、执行数据库存储过程。通常通过CallableStatement实例实现。
具体的实现方式:
Statement stmt = con.createStatement() ; PreparedStatement pstmt = con.prepareStatement(sql) ; CallableStatement cstmt = con.prepareCall("{CALL demoSp(? , ?)}") ;
5、执行SQL语句
Statement接口提供了三种执行SQL语句的方法:executeQuery 、executeUpdate 和execute
1、ResultSet executeQuery(String sqlString):执行查询数据库的SQL语句 ,返回一个结果集(ResultSet)对象。
2、int executeUpdate(String sqlString):用于执行INSERT、UPDATE或 DELETE语句以及SQL DDL语句,如:CREATE TABLE和DROP TABLE等
3、execute(sqlString):用于执行返回多个结果集、多个更新计数或二者组合的 语句。 具体实现的代码:
ResultSet rs = stmt.executeQuery("SELECT * FROM ...") ; int rows = stmt.executeUpdate("INSERT INTO ...") ; boolean flag = stmt.execute(String sql) ;
6、处理结果 两种情况:
1、执行更新返回的是本次操作影响到的记录数。
2、执行查询返回的结果是一个ResultSet对象。
• ResultSet包含符合SQL语句中条件的所有行,并且它通过一套get方法提供了对这些 行中数据的访问。
• 使用结果集(ResultSet)对象的访问方法获取数据:
while(rs.next()){
String name = rs.getString("name") ;
String pass = rs.getString(1) ; // 此方法比较高效
}
(列是从左到右编号的,并且从列1开始)
7、关闭JDBC对象
操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声 明顺序相反:
1、关闭记录集
2、关闭声明
3、关闭连接对象
if(rs != null){ // 关闭记录集
try{
rs.close() ;
}catch(SQLException e){
e.printStackTrace() ;
}
}
if(stmt != null){ // 关闭声明
try{
stmt.close() ;
}catch(SQLException e){
e.printStackTrace() ;
}
}
if(conn != null){ // 关闭连接对象
try{
conn.close() ;
}catch(SQLException e){
e.printStackTrace() ;
}
}

 

4.jdbc如何调用存储过程.

现举例来说明使用JDBC调用存储过程。

  • 编写存储过程(参看MySQL文档)

    delimiter $$	
    					接收一个输入参数
    	create procedure demoSp(in inputParam varchar(255), inout inOutParam varchar(255))
    	begin       在传进来的字符串前加上zyxw---,然后将结果into到第二个参数
    		select concat('zyxw---', inputParam) into inOutParam;
    	end $$
    
    delimiter ;
  • 得到CallableStatement,并调用存储过程

    public class Demo5 {
    
        // 调用存储过程
        public static void main(String[] args) throws SQLException {
    
            Connection conn = null;
            CallableStatement st = null;
            ResultSet rs = null;
    
            try {
                conn = JdbcUtils.getConnection();
                // 得到CallableStatement,并调用存储过程
                st = conn.prepareCall("call demoSp(?,?)");
                // 设置参数,注册返回值,得到输出
                st.setString(1, "叶磊磊");
                st.registerOutParameter(2, Types.VARCHAR); // 告诉MySQL驱动输出的是什么类型,即数据库的类型,Types类中有所有数据库的常见类型 
                st.execute();
                System.out.println(st.getString(2));
    
            } finally {
                JdbcUtils.release(conn, st, rs);
            }
        }
    
    }
  • 运行结果为:zyxw---叶磊磊

学了存储过程,将来对数据进行处理就有2种选择了。

  1. 在数据库里面写存储过程,对数据进行处理,然后在java程序里面直接调用存储过程,获取到处理完后的数据,直接显示即可。
  2. 不在数据库里面写存储过程,通过JDBC获取数据库中的数据,获取到了之后拿到程序里面写方法来处理,处理完后再显示。

什么情况下我们需要在数据库里面写存储过程来处理数据呢? 
答:银行的数据处理一般用存储过程来实现。银行那些业务都是写好存储过程后给你,你去调用存储过程即可。在金融证劵领域用到的特别多,金融证劵领域的开发是不会用hibernate框架,因为表结构不会对开发人员公开

 

5.jdbc如何调用数据库中的视图.

数据库视图使用:
1、插入数据:insert into 视图名(视图列1,视图列2) values(值1、值2)
2、更新数据:update 视图名 set 列名=值 where 更新条件
3、删除数据:delete from 视图名 where 删除条件
4、查询数据:select 要查询的列1,要查询的列2 from 视图名 where 查询条件

总结:视图的用法跟普通表的用法一致

 

6.数据库连接池的工作原理.

1、连接池原理

连接池技术的核心思想是:连接复用,通过建立一个数据库连接池以及一套连接使用、分配、管理策略,使得该连接池中的连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。另外,由于对JDBC中的原始连接进行了封装,从而方便了数据库应用对于连接的使用(特别是对于事务处理),提高了开发

效率,也正是因为这个封装层的存在,隔离了应用的本身的处理逻辑和具体数据库访问逻辑,使应用本身的复用成为可能。连接池主要由三部分组成(如图1所示):连接池的建立、连接池中连接的使用管理、连接池的关闭。下面就着重讨论这三部分及连接池的配置问题。

 

posted @ 2017-06-29 13:04  hdsfakjhf  阅读(401)  评论(0编辑  收藏  举报