12月7日——培训第15天

来源:互联网 发布:rar在linux下解压 编辑:程序博客网 时间:2024/05/13 13:02

今天应该是开始讲Hibernate了吧,昨天换了宿舍,原来那个宿舍虽然说地方大,但是晚上实在是太冷了,
现在换到的地方就是金毅从前的宿舍,也是在3层,现在好了,月租也便宜,屋子也暖和,很不错了……

今天应该是开始讲Hibernate了吧,我还没看呢……
-----------------------------------------------

Hibernate:让数据在关系型的数据库中冬眠。
目前已知的OR解决方案:
1 实体EJB,主要是指CMP方式的实体EJB
2 JDO、java Data Object试图解决EJB存在的问题
3 TopLink,后来被oracle收购
4 Hibernate

持久化persistence:把数据写到文件或数据库中
序列化serilizable:将内存中数据写到外存去成为一个文件。

Hibernate创始人:Gavin King
Hibernate于2001年11月,是他业余时间的个人作品,后被JBoss采纳,现在Gavin king
已经是EJB3.0委员会的成员

www.sf.net;
www.sourceforge.net
上述两个网址里面有相当多的开源项目。

www.hibernate.org  Gavin King的专业hibernate网站。

Hibernate并不向前兼容,比如3.0的用2.0的东西肯定是不行的。
3.1和3.0差别就很大,我们主要讲3.1。

2003年Gavin King加入JBoss,Hibernate有效的利用了
JDBC的特性,优化了性能。Hibernate的核心思想是将数据库中的数据映射到
JavaBean对象上,并可以直接将对象的属性上的数据写到数据库对应的字段上。

Hibernate要解决的问题:
1 怎么连接数据库,连接哪个数据库?
2 javabean中的每一个属性分别与哪一个表的哪个字段相对应
Hibernate要求看不到sql语句和连接数据库的代码。

POJOs(Plain Old Java Objects或Plain Ordinary Java Objects)叫做
Hibernate中用于存储数据的javaBean对象。
POJOs就是一个普通的类,不需要实现任何接口或继承什么类,它不占用资源。
要作的也就是将数据表映射到一个POJOs上面。

EJB必须实现一些接口,能够实现远程操作,这是与javaBean的区别。

Hibernate中给了两个配置文件:
1 Hibernate配置文件
2 Hibernate映射文件

配置文件和映射文件要解决的问题:
1 数据库连接方面的信息。屏蔽不同数据库之间的差异
2 解决对象与表之间的映射

这两种配置均以xml形式来配置。
XML:extensible markup language, 来自SGML

dtd:data type defination数据类型定义文档,用来定义那些你想扩展的标记

xml文档分两部分:xml和dtd。

dtd文档:
注释标记: <!-- -->

<!DOCTYPE后面跟的是根元素
比如<!DOCTYPE ejb-jar
根元素就是ejb-jar

<!ELEMENT hibernate-configuration (session-factory,security?)>
在hibernate-configuration标签中要有session-factory和security两个子标签
里面有一个? 还有逗号。注意xml文档中session-factory一定要在security的前面

,代表序列(一定要按照顺序出现!),|代表或者,
securety?代表这个标签要么不出现,要出现的话就只能出现一次! (0次或1次)
security*表示这个标签可以出现0次或者1次或者多次!    (0次、1次、或多次)
security代表这个标签必须在xml文档中出现一次而且仅能出现一次!(1次)
security+代表这个标签至少出现一次,可以出现多次!(1次或多次)

注意xml中标签必须闭合!
注意不允许<br> ,必须<br/>
或者<br> ……</br>
xml中不能交叉嵌套!

<b>
  <i>fkldas
</b>
  </i>     //这种写法是不允许的!!!

标签可以拥有属性的:<font color="red">fdkas</font>
在xml中属性值必须加上双引号,虽然html中不必如此。

----------------------------------------------------------------------
课间休息时,张老师过来谈了一下希望学员能够有人帮助写书稿,还提到了补贴的问题,一期班里面
没有人愿意写书,我们班里目前倒是有一个愿意写的。张老师目前手里有4、5个书稿,其实每个书稿都可以
当作学习资料下发,但是如果该书稿没有整理出版就下发的话,恐怕会泄漏出去,这样难免有小人会用这个
书稿攒书,张老师的想法是希望能够有学员帮助整理书稿帮助书的出版。

站在他们的角度来讲,这自然是件好事没有错,因为要忙培训授课的事情肯定没时间整理这些东西,
但是站在学员的角度呢?这些书稿可以作为很好的学习资料下发给学员,但是张不会这么作的,因为他
很不希望自己的书稿在没出版前就被人剽窃了去,然后被别人融进自己的书里出版,张是绝对不会容许
这种事情发生的,但是只要书稿一下发给学员,肯定就有可能会被流产到网上,毕竟我们中国是个“诚信”
大国,发生这种事情一点都不稀奇,更何况学员也就这4个月和张孝祥有关系,4个月过了之后各走各的路,
谁与谁有什么相干呢?我凭什么一定要信守那些所谓的不把书稿流传到网上的承诺呢?好像即便我违背了
承诺也丝毫没什么关系不是么?张知道这一点,学员也知道,大家都心知肚明,书稿是断然不会
给我们看的,这一点上根本没有悬念。那么既然如此,张孝祥还何必一定要提出什么书稿如果下发给学员的
话,会是很好的学习资料这种事情呢?无非是为了能够让一些学员加入进整理书稿的队伍,让这些教材尽快
的出版而已。

可这就又有了一个问题,张老师和田老师可能没有太想清楚,来这里培训的学员目的是什么?是为了帮助
老师整理书稿么?可能老师会说了,你帮我整理书稿,本身就是再一次的学习。这话是没错,整理书稿的过程
中融合进了自己的体会,等于是写了一遍读书笔记似的,绝对比自己单在那里看书要进步大得多。但是呢,
你可以去问问任何一个现在在IT业界工作的人这是不是一个好主意,谁都知道工作中的学习锻炼与这些所谓
的帮助老师整理书稿的学习到底谁优谁劣,任何一个初入IT的人都应该找家IT企业公司工作,在工作中锻炼自己
的能力和技术,同时也是对未来发展的一个展望,在工作的过程中可以更深的了解整个中国的IT业界情况,同时
软件开发还可以扩展自己的其他领域的知识,这都是事实。

如果培训结束之后不去公司就业,反倒留下来培着某培训机构的老师整理一些书稿或是从事一些助教的工作那成什么了??
在职业生涯规划还没有起步前就过早的把自己栓在了软件培训上面难道不草率么?如果整理书稿是一种学习锻炼的话,
那么也只会对自己未来的出书有帮助,试问一下,到什么时候出书才合适呢?你是要出书还是要攒书呢?正经从事软件开发
工作的人是不会去写书的,也没有时间去写,写的人只可能是那些从事软件培训或是大学老师的家伙,这也就是涉及一个
职业生涯定位的问题,不管如何,以后留在某培训机构给老师整理书稿可以说是职业生涯中败笔的败笔(当然我这是对于
从来没有进入IT行业工作的人来说的,我也是一样),是得不偿失的,这也是为什么一期班里没有一个人愿意帮老师整理
书稿的原因,大家是为了技术而来,不是为了天下更多的IT学习者能够得到一本好的教材,毕竟现在好的教材已经太多
太多了。

-------------------------------------------
<!ELEMENT property(#PCDATA)>正面这个元素标签中可以加入字符串
讲义里面的configuration里面有配置的dtd文件。
eclipse中导入lib中的所有包以及mysql驱动jar包

新建一个类:
public class User
{
 private int id;
 private String username ;
 private String password;
 private Timestamp loginTime;
 private int loginTimes ;

 然后加入getter和setter方法,代码略……
 
};

新建一个File,叫做hibernate.cfg.xml,必须这么起名,原因还不明。
然后输入一下的代码(参考Hibernate3.0的配置dtd文件)
<?xml version="1.0"?>
<!DOCTYPE ………………………………可以拷贝dtd中的那一段>

<!DOCTYPE hibernate-configuration PUBLIC
 "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
//注意:下面的这些属性名称可以在老师给的讲义中的configuration中的property配置文件中找到
//当然也可以在官方文档的第三章节的configuration中看到
<hibernate-configuration>
  <session-factory>
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>//这个包类在hibernateAPI3.1中有

 <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
 <property name="connection.url">jdbc:mysql:///j2ee</property>
 <property name="connection.username">root</property>
 <property name="connection.password">root</property>

 <property name="show_sql">true</property>
  </session-factory>
</hibernate-configuration>
----------------------------
//接下来还需要一个映射文件:可以参考3.0的dtd文档
//注意映射文件必须和类建在一个包里面,而且刚才那个个hibernate.cfg.xml要放在类路径的根路径下,也就是工程目录下
也就是必须命名为User.hbm.xml     (hbm是hibernate mapping的缩写)

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<!--
 指明类和表之间的映射关系
-->
  <class name="包名.User" table="users">
 <id name="id" column="id"> //注意:这里的id是主键,name是User类里面的属性名,column是数据表中的对应列名。
 <!-- 上一句话指明对象标识与主键之间的映射关系 -->
   <generator class="identity"/> //generator是主键生成器的意思。指明主键是如何生成的。
 </id>

 <property name="username" column="username"/>
 <property name="password" column="password"/>
 <property name="loginTimes" column="loginTimes" type="int"/> //type可以不填写
 <property name="loginTime" column="loginTime"/>
  </class>
</hibernate-mapping>

---------------------------------------------

import org.hibernate.cfg.Configuration;
public class Demo //这个类不必和刚才的Users放在一个包中
{
 public static void main(String[] args)
 {
  Configuration cfg = new Configuration();//创建一个配置,与Hibernate.cfg.xml相对应。
  cfg.addClass(User.class); //把User引进来,先要读cfg.xml的配置文件,然后读hbm.xml来看映射关系。
          //让程序根据类名去找配置文件,这时生成了有关User的全部增删改查的sql语句
          //这些预先生成的sql语句存在SessionFactory中。

  //将已经映射过的实体(和数据库中表对应的实体类)加入到配置中去。

  cfg.configure();//这就把读入的两个配置文件统一的编译一下,把连接什么的配置好,将信息读入到内存中

  //创建会话工厂。
  SessionFactory factory = cfg.buildSessionFactory();//工厂模式,用于创建会话。

  //由会话工厂创建会话,因为Session是接口,所以不能直接new,只能通过方法来得到。
  //注意这里导入的包是org.hibernate.Session!
  Session session = factory.openSession();

  //创建一个实体对象(可选步骤)
  User u = new User();
  u.setUsername("Gavin");
  u.setPassword("King");
  u.setLoginTime(new Timestamp(System.currentTimeMillis()));
  u.setLoginTimes(1);

  //开始一个事务
  session.beginTransaction();
  
  //8 对实体进行操作
  session.save(u); //实现数据库的插入操作

  //9 提交事务
  session.getTransaction().commit();
  
  //10 关闭会话
  session.close();
 }
};
------------------------------------
中午得把这个程序原封不动的重写一遍,熟悉一下!试了一下,还挺顺利的,出来了。
张老师中午来通知聚餐的事情,我还以为没戏了呢,呵呵,据说要到晚上9点左右,
别人掏钱买车票,哈哈,赚了
-------------------------------------

hibernate.cfg.xml:

<?xml version="1.0"?>
<!DOCTYPE ………………………………可以拷贝dtd中的那一段>

<!DOCTYPE hibernate-configuration PUBLIC
 "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
//注意:下面的这些属性名称可以在老师给的讲义中的configuration中的property配置文件中找到
//当然也可以在官方文档的第三章节的configuration中看到
<hibernate-configuration>
  <session-factory>
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>//这个包类在hibernateAPI3.1中有

 <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
 <property name="connection.url">jdbc:mysql:///j2ee</property>
 <property name="connection.username">root</property>
 <property name="connection.password">root</property>

 <property name="show_sql">true</property>
 <property name="format_sql">true</property> //作用是让显示出的sql语句按照一定格式排列
  </session-factory>
</hibernate-configuration>
-------------------------------
Serializable也是空接口,和clone一样用于标识类型的
可序列化:是对于对象来说的,可以从内存写到外存中去。

改写Demo类,实现数据库的读取:
Configuration cfg = new Configuration();
cfg.addClass(User.class);
        
cfg.configure();  
SessionFactory factory = cfg.buildSessionFactory();  
Session session = null;

try
{
 session = factory.openSession();
 User u = (User)session.load(User.class, new Integer(2));//也就是读第二条记录!!
 System.out.println("/n用户名是"+u.getUsername());
}
catch(Exception e)
{
 e.printStackTrace();
}
finally
{
 session.close();
}

------------------------------------------
实现数据库更新:

//如果每个列都要更改的话,就没必要用动态更新了。

//如果要实现动态更新的话,在映射配置文件中的class元素中的name和table属性后面增加dynamic-update="true"的代码
try
{
 session = factory.openSession();
 session.beginTransaction();
 User u = (User)session.load(User.class, new Integer(2));//也就是更新第二条记录!!2在这里其实是主键的名称!!!
 u.setUsername("aa");         //更改用户名为aa,注意如果不用动态更新的话,主键为2的记录全部字段都会被改为aa!!!!
 u.setPassword("aajava");   //这里会和上面的set一起执行!要注意动态更新与静态更新的区别!!!!

 System.out.println("/n用户名是"+u.getUsername());
 session.getTransaction().commit();
}

//动态更新的话先要看谁被改了,然后再拼sql语句。

Hibernate肯定不如直接用JDBC性能高,但是它应用了许多JDBC中可以优化性能的东西,而这些东西很少有人实际应用在jdbc中,
开发效率会高于jdbc,但是执行效率低于jdbc。

-------------------------------------------------------
删除一条记录。

try
{
 session = factory.openSession();
 session.beginTransaction();
 User u = (User)session.load(User.class, new Integer(2));
 session.delete(u);        
 
 System.out.println("/n用户名是"+u.getUsername());
 session.getTransaction().commit();
}

----------------------------------
实现插入的另一种方法:
try
{
 session = factory.openSession();
 session.beginTransaction();
 User u = new User();
 
 session.persist(u); //ejb3.0没有save这个方法,使用persist方法存对象是ejb3.0的标准,hibernate只是为了
      //和其保持一致而已。
 
 
 session.getTransaction().commit();
}
-------------------------
实现选取的另一种方法。
try
{
 session = factory.openSession();
 
 User u = (User)session.get(User.class,new Integer(3)) //这里没有用load,而是用了get,也是把主键为3的记录选取出来。
              //load可以实现延迟加载,get不能够实现延迟加载。

 System.out.println(u.getUsername());
 
 
 session.getTransaction().commit();
}
-------------------------------------
更新的另外一种形式:
try{
 session = factory.openSession();
 session.beginTransaction();
 User u = new User();

 u.setId(3);         //一定要提供主键才可以更新,要不然谁知道是哪条记录呢??
 u.setUsername("aa");
 session.update(u);

 session.getTransaction().commit();
}

---------------------------------
插入:1 save方法 2 persist方法
更新:1 load方法后用set方法更新 2 先set后update方法
查询:1 load方法 2 get方法。
---------------------------------
有关延迟加载的说明:
User u = (User)session.load(User.class,new Integer(3));
如果后面不对u进行任何操作的话,那么不会去读数据库。除非你对u进行操作,
也就是它要等待你对u进行操作之后才去加载数据库中的数据,这就是所谓的延迟加载。
比如System.out.println(u.getUsername);

但是如果是User u = (User)session.get(User.class,new Integer(3));
那么不管后面对u进行不进行操作,都会去读数据库

那么如何实现延迟加载呢?也就是说调用对象的getter方法如何通知数据库去取数据呢??

其实使用的是代理模式:我虽然对u进行操作,但是暗中已经被hibernate替换成了代理对象,
对u的操作实际上是对相应的代理进行操作!当你在User u = (User)session.get(User.class,new Integer(3));
这句话后面加上一个System.out.println(u.getClass());来观察一下结果
会看到一个代理的对象,也就是在session.load方法或是session.get方法中返回的不是对象u,而是一个代理对象。
注意,如果使用的是session.load方法得到u的话,即便是后面用了u.getClass()方法,也是不会去读取数据库的!

代理对象:class vo.User$$EnhancerByCGLIB$$de3ae582

CGLIB:code generator library

代理实现是用讲义Lib中的cglib-2.1.3.jar包来实现的,这个包里面的机制目前班里还没有人知道。
------------------------------
-----------------------------
User u = (User)session.load(User.class,new Integer(100));
User u2 = (User)session.get(User.class,new Integer(100));

注意:如果没有上面的第一行代码,u2是null值!
但是由于有了第一行代码,延迟机制已经返回了一个代理对象u并且存入了缓存,
执行get方法就是去用了那个u对象,执行后u2和u是一个东西!
------------------
如果上述两行代码颠倒的话,如下所示:
User u2 = (User)session.get(User.class,new Integer(100));
User u = (User)session.load(User.class,new Integer(100));
则u2是null值,u1会得到一个代理的对象
------------------
User u = (User)session.load(User.class,new Integer(100));
User u2 = (User)session.get(User.class,new Integer(200));
这样一来,u是个代理对象在缓存中,但是由于u2找的主键是200,和缓存中的u的
主键不同,所以不会拿u的对象来用,还是得读数据库,于是呢,由于和数据库
有了交互,u2就是null值。
------------------
如果设置了:dynamic-update="true"

第一种更新写法:
try
{
 session = factory.openSession();
 session.beginTransaction();

 User u = new User();
 u.setId(3);
 u.setUsername("aa");
 session.update(u);

 session.getTransaction().commit();
}
最后生成的更新语句中所有字段更新都有
也就是说这种声明一个新对象来通过设置主键的方式来
更新数据库中记录的方法是不受dynamic-update限制的!!


第二种更新写法
try
{
 session = factory.openSession();
 session.beginTransaction();

 User u = (User)session.load(User.class,new Integer(3));//放入缓存中
 u.setUsername("bb");
 session.getTransaction().commit();
}
最后生成的更新语句只有username被更新
也就是说这种通过session加载某条记录给一个对象,并通过这个对象来更新的方式
是可以通过dynamic-update来限制的。

特别注意:上面的两个更新我得再说明一下,最好不要用第一种写法,因为第一种写法和
插入很是相似,等价于先删除指定的记录,然后再用新的对象来插入到刚才删除记录的位置。

而第二种写法是将指定的记录加载出来,在它的基础上进行更新。

如果用了第一种方法的话,那么表里面的登陆次数(默认为是int型),假如指定的记录里面
对应的登陆次数是2,那么如果没有在第一种记录中指定u.setLoginTimes(2)的话,一旦用
第一种方法进行更新的话,你会发现登陆次数被改为了0,因为对象int类型初始值就是0,
而Timestamp时间类型也会默认初始化为当前系统的时间。

用第二种方法的话,不管你是否设置dynamic-update值为true,都不会去更改原来的登陆次数!
但是如果dynamic-update值为false的话,登陆时间会被更改。

也就是说,用第二种更新方法的话,只要不涉及到Timestamp的数据类型,


顺带一提:
User.class:等价于new User().getClass()和Class.forName(User)。
-----------------------------------------
-----------------------------------------
//脱管状态:有主键,但是和session不发生关联
//临时状态:没有主键,也和session不发生关联。
User u = new User();
u.setUsername("china");
u.setPassword("nihao");

session.persist(u);   //persist不能对脱管状态对象进行操作!!

//注意:我创建了一个对象,这个对象如果对应的表中有个主键叫做id的话,那么如果我用setId方法为
//该对象的id属性进行赋值的话,那么该对象就有了主键,就是脱管状态了!否则就是临时状态!!!

---------------------------------------------
---------------------------------------------
今天这些乱七八糟的东西的总结

插入:save                   persist 
   区别persist只会持久化处于临时状态的对象,
     save无论对象是否处于临时状态都会向数据库中保存。
更新:load->setter       update
   区别前者可以实现动态更新,即dynamic-update
     后者因为在缓存中没有可比较对象,所以不支持动态更新。
查询:load                   get
   前者支持延迟加载,后者不支持。
   前者在数据库中不存在记录时返回一个代理对象
   后者则返回一个null

延迟加载

代理模式
CGLIB code generator libary


SessionFactory factory = new Configuration().addClass(User.class).configure().buildSessionFactory();

 


-----------------------------------------------------------------------------
晚上是聚餐,我们去了上地桥那里的某培训机构公司看了一下,很小的一个地方,还没成规模吧,吃饭的地方是外面的一家餐馆,名字
忘了,我从来也不去记那些,自己对吃什么的从来没什么兴趣,也没什么讲究,比较喜欢的也就是学校食堂的那些家常菜,像这些正经
餐馆什么的,总觉得没什么好吃的东西,还蛮贵……算了,反正也不是自己掏钱,在乎什么呢?

我们在那里等了很长的时间,因为老师们一直没有到场,一期的学员也来了,我们是分着坐的,这和老师当时的初衷是相悖的,本来
是希望我们能够穿插着坐,每一桌既有一期的学员又有二期的学员,这样方便大家的沟通和交流……但是呢,中国人的群体意识还是比较
浓的,自己班的还是比较喜欢和自己班的人坐在一起,好像是默守了这个规矩吧……

老师来了这么几个:教员张孝祥、田雪松、梁言兵,助教方立军,王晶,某培训机构公司里面的两位职员(我也不知道他们具体是谁,其中
一个人是带我和金毅去看过房子的人,另外一个是在开学典礼上致恶心开幕词的人)。过程嘛……中国人也都知道,传统的敬酒、敬酒、还是
敬酒,我们中国的酒文化是个很有意思的东西,这中饮料似乎对我们中国人来说有着外人所不能了解的别样的意义,餐桌上的敬酒如果不理会
的话是很不礼貌的行为,而且如果不全部倒满喝光的话有时也会让敬酒的人大为恼火,酒能解决很多问题,酒桌上产生友谊,倾吐真言也很方便,
而且有些问题没有酒还真办不成,但是酒喝多了又很影响身体,有时真让人感慨:人类啊……真是奇怪的生物……

听田老师和张老师说了许多,感觉教员是很负责任的,这就行了,和大家聊的也很投机,我这个人以前是很不擅长应付这种场面的,但是
现在已经好多了,呵呵……