实现Product和Category多对一的关系
1.在Product类中增加Category属性
2.在Product.hbm.xml中设置Category多对一的关系
<many-to-one name="category" class="Category" column="cid" />
name="category"对应Product类中的category属性
class="Category"表示对应Category类
column="cid"表示指向category_表的外键,即cid是category_表的外键
3.在hibernate.cfg.xml中增加Category的映射(与Product相似)
增加了一个新的Category对象c1,并将其设置成id=8的product的category
Category c =
new
Category();
c.setName(
"c1"
);
s.save(c);
Product p = (Product) s.get(Product.
class
,
8
);
p.setCategory(c);
s.update(p)
实现Category和Product一对多的关系
1.在Product中增加一个Set的集合
2.在Category.hbm.xml中增加one-to-many的映射
lazy=“false”表示不使用延迟加载
not-null=false表示Product相对于Category的外键是cid且可以为空
实现Product和User之间多对多的关系
1.User中维护一个Product的集合
2.在相应的.hbm.xml中增加了many-to-many的映射
<
set
name
=
"products"
table
=
"user_product"
lazy
=
"false"
>
<
key
column
=
"uid"
/>
<
many-to-many
column
=
"pid"
class
=
"Product"
/>
</
set
>
突然发现并不一定要用SQL语句手动创建表而只需要在文件中进行配置就可以了……
果然在教程的评论里发现有人和我有一样的疑问……
解释是:在hibernate.cfg.xml配置文件里有一个 <property name="hbm2ddl.auto">update</property>, 会导致表结构自动创建和修改,以前只以为update是自动更新相关数据的意思,没想到还有自动创建表的功能
还有额外的收获是有人注意到了虽然User和Product保持着多对多的关系,但是product的user属性却是null,这体现出了两者之间的关系只能通过中间表来体现,这就是和一对多,多对一关系的不同之处了
Hibernate任何对数据有改动的操作,都应该放在事务里面,如前文提到的那样,这要求MySQL是支持InnoDB的
Hibernate的延迟加载
属性延迟加载:当使用load方法来获取对象的时候,只有当访问了这个对象的属性,Hibernate才会到数据库中查询,否则并不会访问数据库。
关系的延迟加载:当要获取category_表的信息的时候,并不会查询product_表,只有通过category获取product的时候,才会进行对于product_表的查询。
级联操作:在没有配置级联的时候,删除分类,其分类下对应的产品并不会被删除
级联有四种类型:1.all:所有操作都执行级联操作;2.none:所有操作都不支持级联操作
3.delete:删除时执行级联操作;在Category中:
<set name="products" cascade="delete" lazy="false">
4.save-update:保存和更新时执行级联操作
级联操作通常用于one-many和many-to-many上,几乎不会用在many-one上
<set name="products" cascade="save-update" lazy="false">
会把没有添加到数据库里的瞬时状态的产品对象持久化
Hibernate的一级缓存是在Session上,二级缓存是在SessionFactory上
Hibernate使用Criteria进行分页查询
Criteria c= s.createCriteria(Product.
class
);
c.add(Restrictions.like(
"name"
,
"%"
+name+
"%"
));
c.setFirstResult(
2
);
c.setMaxResults(
5
);
表示从第二条数据开始,一共查询五条数据
get和load两种获取方式:对于id不存在的情况,get方法会返回null,load方法会抛出异常
1. 获取的是否是同一个session对象
openSession每次都会得到一个新的Session对象
getCurrentSession在同一个线程中,每次都是获取相同的Session对象,但是在不同的线程中获取的是不同的Session对象
2. 事务提交的必要性
openSession只有在增加,删除,修改的时候需要事务,查询时不需要的
getCurrentSession是所有操作都必须放在事务中进行,并且提交事务后,session就自动关闭,不能够再进行关闭
Hibernate有缓存机制,可以通过用id作为key把product对象保存在缓存中
同时hibernate也提供Query的查询方式。假设数据库中有100条记录,其中有30条记录在缓存中,但是使用Query的list方法,就会所有的100条数据都从数据库中查询,而无视这30条缓存中的记录
N+1是什么意思呢,首先执行一条sql语句,去查询这100条记录,但是,只返回这100条记录的ID
然后再根据id,进行进一步查询。
如果id在缓存中,就从缓存中获取product对象了,否则再从数据库中获取
s.beginTransaction();
String name =
"iphone"
;
Query q =s.createQuery(
"from Product p where p.name like ?"
);
q.setString(
0
,
"%"
+name+
"%"
);
Iterator<Product> it= q.iterate();
while
(it.hasNext()){
Product p =it.next();
System.out.println(p.getName());
}
s.getTransaction().commit();
突然联想到基于1的和基于0的,返回查询发现是PreparedStatement模糊查询是基于1的
PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?"); pstmt.setBigDecimal(1, 153833.00)返回查询总数:
s.beginTransaction();
String name =
"iphone"
;
Query q =s.createQuery(
"select count(*) from Product p where p.name like ?"
);
q.setString(
0
,
"%"
+name+
"%"
);
long
total= (Long) q.uniqueResult();
System.out.println(total);
s.getTransaction().commit();
增加一个version字段,用于版本信息控制。这就是乐观锁的核心机制。
比如session1获取product1的时候,version=1。 那么session1更新product1的时候,就需要确保version还是1才可以进行更新,并且更新结束后,把version改为2。
注意: version元素必须紧跟着id后面,否则会出错。
1. 假设数据库中产品的价格是10000,version是10
2. session1,session2分别获取了该对象
3. 都修改了对象的价格
4. session1试图保存到数据库,检测version依旧=10,成功保存,并把version修改为11
5. session2试图保存到数据库,检测version=11,说明该数据已经被其他人动过了。 保存失败,抛出异常
C3P0连接池
Hibernate的注解:本来放在.hbm.xml中的文件映射信息,不用配置文件而改用注解来完成
MVC
仅仅使用Servlet的短处:不仅要准备数据,还要准备HTML,可读性很差,写起来也很麻烦
仅仅使用JSP的短处:写Java代码不如在Servlet中方便
结合Servlet和JSP
public
class
HeroEditServlet
extends
HttpServlet {
protected
void
service(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
int
id = Integer.parseInt(request.getParameter(
"id"
));
Hero hero =
new
HeroDAO().get(id);
request.setAttribute(
"hero"
, hero);
request.getRequestDispatcher(
"editHero.jsp"
).forward(request, response);
}
}
实现获取数据然后跳转到jsp页面
<%@ page language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
import
=
"java.util.*,bean.*,java.sql.*"
%>
<form action=
'updateHero'
method=
'post'
>
名字 : <input type=
'text'
name=
'name'
value=
'${hero.name}'
> <br>
血量 :<input type=
'text'
name=
'hp'
value=
'${hero.hp}'
> <br>
伤害: <input type=
'text'
name=
'damage'
value=
'${hero.damage}'
> <br>
<input type=
'hidden'
name=
'id'
value=
'${hero.id}'
>
<input type=
'submit'
value=
'更新'
>
</form>
不做查询数据库的事情,直接获取从HeroEditServlet传过来的Hero对象
MVC Model View Controller
控制器的作用就是把不同的数据,显示在不同的视图上
DAO:
把驱动的初始化的方法放在HeroDAO中
提供一个getConnection()的方法返回连接:所有的数据库操作都需要事先拿到一个数据库连接Connection
验证用户是否登录
从session中取出userName,如果是空,就表示用户没有登录,或者登录已经超过了30分钟。 客户端跳转到login.html,让用户重新登陆
struts
1.所有的访问都会被web.xml中配置的Struts的Filter所拦截
2.拦截之后,就进入Struts的工作流程
3.访问的地址是/index,根据Struts按照struts.xml的配置,服务器跳转到index.jsp
4.显示index.jsp的内容
在struts中也可以获取Servlet包中的request和response对象
struts中的Session有两个
一个是传统的servlet包下的HttpSession
另一个是Struts中自己定义的Session
传统的servlet包下的session的获取办法是:
ServletActionContext.getRequest().getSession();
使用该方法,需要在eclipse的项目中导入servlet-api.jar,可以在右边下载
新的Session的获取办法是
Map m = ActionContext.getContext().getSession();
这个session以Map类的形式出现,其中的值和HttpSession中的值是同步的
struts的专属标签库
form标签用于提交数据
<
s:form
action
=
"addProduct"
>
<
s:textfield
name
=
"product.name"
label
=
"product name"
/>
<
s:submit
value
=
"Submit"
/>
</
s:form
>
提供s:iterator用于遍历一个集合中的数据
1.为ProductAction增加list()方法
为ProductAction增加一个products属性,类型是List,并提供get()和set()方法
为ProductAction增加一个list()方法,为products添加3个product对象,并返回list
2.在struts.xml中配置路径listProduct,并返回list.jsp
通配符匹配
拦截器:
package
com.how2java.interceptor;
import
java.util.Date;
import
com.how2java.action.ProductAction;
import
com.opensymphony.xwork2.ActionInvocation;
import
com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public
class
DateInterceptor
extends
AbstractInterceptor {
public
String intercept(ActionInvocation invocation)
throws
Exception {
ProductAction action = (ProductAction)invocation.getAction();
action.setDate(
new
Date());
return
invocation.invoke();
}
}
表单验证
注解