在传智四个月的学习已经结束一段时间了。我也终于找到了自己满意的工作。现在回头想想,我反倒觉得非常遗憾,以为早在07年我就曾经有过来传智播客学习的打算,可是后来以为自己从学校获得了去一个小软件公司实习的机会而放弃了。如今两年过去了。我最终还是选择来到传智播客。可是我却无可奈何的浪费了两年的时间。有时候真的在想,如果我两年前就选择了来这里,我现在所取得的一切一定在两年前就可以获得了。后悔,但是也无可奈何。
最初注意到传智播客是在网上下载的视频,当时抱着姑且看一看的想法就下载下来看了。没想到真的看了以后发现每学习一个新的视频总是会有新的收获。而这些收获是在学校的教授的课程中根本无法学习到得,这并不是说大学老师的课不好,而是说两者完全是不同的风格。我当时看的是张孝祥老师的视频,感觉他似乎总能使用一些通俗易懂的类比把一些非常复杂和抽象的概念和设计理念说清楚。我经常在想,如果我得大学的老师能多几个想张老师这样能把复杂概念用如此通俗易懂的方式讲清楚的人,那我们上学一定会轻松很多。
下面就说说在传智播客学习的情
* 工作流管理系统和jBPM
** 工作流(Workflow)
就是自动运作的业务过程部分或整体,表现为参与者对文件、信息或任务按照规
程采取行动,并令其在参与者之间传递。简单地说,工作流就是一系列相互衔接、
自动进行的业务活动或任务。工作流是针对工作中具有固定程序的常规活动而提
出的一个概念。通过将工作活动分解成定义良好的任务、角色、规则和过程来进
行执行和监控,达到提高生产组织水平和工作效率的目的。
工作流就是工作流程的计算机化,即将工作流程中的工作如何前后组织在一起的
逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。工作流要解决的
主要问题是:为实现某个业务目标,在多个参与者之间,利用计算机,按某种预
定规则自动传递文档、信息或者任务。
通俗的说,就是多个人在一起合作完成某件事情。
** 工作流管理系统(Workflow Management System, WfMS)
主要功能是通过计算机技术的支持去定义、执行和管理工作流,协调工作流执
行过程中工作之间以及群体成员之间的信息交互。工作流需要依靠工作流管理系
统来实现。
工作流管理系统
如何对表单数据进行转换
问题:我们从表单中获取的数据全部都是String类型的,在struts中,我们需要把他们转化成对应的int,float等类型,对于简单类型,struts本身就能自动对其转化,但是对于复杂的数据类型,比如java.util.Date类型就不行了。
对于这种数据转化问题,一种常见的办法是自己手动编写类型转换器,比如在一个servlet的init方法中注册这个自己写的类型转换器,让这个servlet在应用程序启动的时候自动运行。日入下面这个事一个Date类型的自动转化器。这个事在实现ServletContextListener接口的类中注册的
public void contextInitialized(ServletContextEvent arg0)
{
// TODO Auto-generated method stub
ConvertUtils.register(new Converter(){
public Object convert(Class arg0, Object arg1)
{
SimpleDateFormat sdf = new
SimpleDateFormat('yyyy-MM-dd');
try {
System.out.println('AppServletContextListener
--> ' + arg1);
悲观锁和乐观锁
悲观锁和乐观锁就是为了解决数据库的并发性,数据库的隔离级别就是和并发性紧密相关的。数据库隔离级别越高,并发性性也越差。
悲观锁:在一个用户对一个数据进行查看或修改的时候,在他放弃对数据的占有前,其他用户无法操作该数据。加锁以后,lazy属性会失效。
悲观锁的实现,通常依赖于数据库机制,在整个过程中将数据锁定,其它任何用户都不能读取或修改。如果在加锁期间有其他进程需要访问该数据,则会一直等待,直到锁被释放或者超时。
所以悲观锁的并发性不好。
乐观锁:不是锁,是一种冲突检测手段。任何人可以随便改,保存的时候再检查。乐观锁也支持lazy,大多采用版本控制Verson来进行。少数情况下也有用时间戳控制的。
每次更新数据时候对verson+1.如果当前Verson小于等于数据库中的Verson,就认为当前数据是老版本数据,不准许提交。
通过版本来进行控制,每次对数据进行修改以后,数据库中数据都会有一个新的版本号。只有获得了当前最新的版本以后再修改的内容才可以保存进数据库并获得新的版本号。
比如A用户获得了版本1,再其操作过程中数据库中的版本被他人修改到了版本2.。这
Comnoponent映射
component映射
在hibernate中,component是某个实体的逻辑组成部分,它与实体的根本区别是没有oid,
component可以成为是值对象(DDD)
采用component映射的好处:它实现了对象模型的细粒度划分,层次会更分明,复用率会更高,实际映射成的数据库字段还是老样子
现在我们假设下面的场景
有一个User和Employee类,两者都有完全相同的一些字段保存联系方式,比如email,phone,address等。这个时候,为了在对象模型使类结构看起来更清晰,我们可以把这些公共的字段单端创建一个联系方式类:Contract类,定义User和Employee类的时候引用上这个Contact就可以了
下面是实例代码
User
public class User {
private int id;
private String name;
注意这个引用字段
private Contact contact;
}
Contact类
public class Contact {
private String email;
private String address;
private String zipCode;
private String contactTel;
}
这么做数据库中还是把
Many-to-many(user--->role)
对象模型:
两者关联需要一个中间表
类的设计
Role:
public class Role {
private int id;
private String name;
}
其对应的映射文件,由于这一段不维护关系,所以都是普通属性
<hibernate-mapping>
<class name='com.itcast.hibernate.Role'
table='t_role'>
<id name='id'>
<generator class='native'/>
</id>
<property name='name'/>
</class>
</hibernate-mapping>
User类的设计:
public class User {
private int id;
private String name;
private Set roles;
}
注意这里的映射文件:
<hibernate-mapping>
<class name='com.itcast.hibernate.User'
table='t_user'>
<id name='id'>
<generator class='native'/>
Many-to-many(user--->role)
对象模型:
两者关联需要一个中间表
类的设计
Role:
public class Role {
private int id;
private String name;
}
其对应的映射文件,由于这一段不维护关系,所以都是普通属性
<hibernate-mapping>
<class name='com.itcast.hibernate.Role'
table='t_role'>
<id name='id'>
<generator class='native'/>
</id>
<property name='name'/>
</class>
</hibernate-mapping>
User类的设计:
public class User {
private int id;
private String name;
private Set roles;
}
注意这里的映射文件:
<hibernate-mapping>
<class name='com.itcast.hibernate.User'
table='t_user'>
<id name='id'>
<generator class='native'/>
One-to-many(单向)
hihernate一对多关联映射(单向Classes----->Student)
一对多关联映射利用了多对一关联映射原理
多对一关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是多指向一
一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多
也就是说一对多和多对一的映射策略是一样的,都是在多的一段加一个字段,只是站的角度不同
在一一端维护关系的缺点:
如果将t_student表里的classesid字段设置为非空,则无法保存
因为不是在student这一端维护关系,所以student不知道是哪个班的,
所以需要发出多余的update语句来更新关系
下面是对象模型,这次是在一的一段维护这个关系,所以多的一端Student里面只有最基本的属性
public class Student {
private int id;
private String name;
}
在多的一端需要维护和student的关系,内部需要一个集合类型来保存属于自己的所有student,这个通常使用Set接口类型
public class Classes {
private int id;
priv
Session.flush和session.evict方法一起使用出现的问题
Flush:清空临时存储区Insertion,把缓存中的existsInDataBase=true,然后发出sql语句
Evict:清空缓存的EntiryEntires 中数据,但是不清空临时存储区Insertion的数据
Uuid生成策略下,在save以后,在Habernate的临时存储区Insertion和缓存中都会有数据,但是都没有提交到数据库existsInDataBase=false,这个时候如果调用evict,就会把缓存EntiryEntires
中的数据全部清除,但是却不会清除临时存储区中的内容。这个时候当执行commit的时候,session在临时存储区中发现了数据就会执行sql。然后会去缓存中更新缓存中的数据,把existsInDataBase=true.但是这个时候缓存中的数据已经被evict清除了。找不到existsInDataBase。这个时候Habernate会报线程安全异常。
这是因为在Session在清理缓存时,会去临时存储区Insertion取出数据进行insert操作后,需要更新EntiryEntires
中的existsInDataBase属性为TRUE,如果找不到这个属性,就会报异常,而我们的evict会把所有信息从EntiryEntires
清除了。所以找不到相关数据就抛异常
例如使用如下代码
//因为user
一对一(one-to-one)唯一外键关联
唯一外键关联对实体对象的要求和前面是一样的,不过表字段间关系发生了变化
下面我们采用让Person表中保存 一个IdCard的主键的引用,同时把这个引用的外键设置为唯一的。
实体类我们这么定义:
public class Person {
private int id;
private String name;
内部持有对IdCard的引用
private IdCard idCard;
}
public class IdCard {
private int id;
private String cardNo;
内部持有对person的引用
private Person person;
}
Person端映射文件
<hibernate-mapping>
<class name='com.itcast.hibernate.Person'
table='t_person'>
<id name='id'>
主键不再根据IdCard生成了
<generator class='native'/>
</id>
<property name='name'/>
<many-to-one name='idCard' unique='true'/>
</cla