本文将介绍struts2和hibernate在项目中的实际应用
建立pojo类,使用注解配置hibernate映射
在需求分析的时候我们知道需要保存用户的聊天记录,那么怎么保存呢?
我有想过几种方法,比如文件存储,会话存储..但我在实际上操作大都不太可行。于是这次还是简单的把聊天记录储存到mysql数据库中…..
好的,接下来我们要建两个实体类。User和ChatRecord类。
User类用来保存用户信息,一般来说有这么几个属性:
private int id ;//用户id主键
private String name;//用户名
private String avatar;//头像路径
private int sex;//0 for male 1 for female
private Timestamp registerTime;//注册时间
private Timestamp loginTime;//登录时间
但是我们要使用hibernate进行dao层操作,因此在这个实体类中要配置一对多关系映射。毕竟一个用户的聊天记录一般不止一个,与当前用户对应的聊天记录应该是0…*
所以我们还要增加一个属性
private Set<ChatRecord> chatRecords =new HashSet<>();
当然为了方便实例化这个对象,我们还需要设计构造方法
public User() {
}
public User(String name, String avatar, int sex) {
this.name = name;
this.avatar = avatar;
this.sex = sex;
}
使用注解配置实体类的方法也特别简单,这里简单解释一下代码中使用到的注解的作用
- @Entity 标注当前类为实体类
- @Table 建立与数据库中表的连接
- 我们一般不在属性前使用注解,这里我们在get方法前添加注解
- @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
声明数据库表主键,设置数据自增。这里的strategy有多种策略,一般我们使用上面这种 - @Column 配置列,保持类型与java类型一致
- @Column(name = “registerTime”,columnDefinition = “timestamp default CURRENT_TIMESTAMP”) 对数据库对应列的类型自定义,并且设置默认值。这里是默认使用当前时间。
insertable=false表明在插入数据时无需传入当前注解配置的属性(这里指loginTime) - @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER,mappedBy=”sender”) 配置一对多关系映射,cascadeType设置级联操作,,mappedBy=”sender”注明外键。在ChatRecord类中我们将创建这个sender属性与之对应。
这个时候,User类的代码应该是这样的:
package cn.zipple.entity;
import javax.persistence.*;
import java.sql.Timestamp;
import java.util.HashSet;
import java.util.Set;
/**
* Created by zipple on 2017/11/13.
* 用户实体
*/
@Entity
@Table(name = "users")
public class User {
private int id ;
private String name;
private String avatar;//头像路径
private int sex;//0 for male 1 for female
private Timestamp registerTime;
private Timestamp loginTime;
private Set<ChatRecord> chatRecords =new HashSet<>();
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "avatar")
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
@Column(name = "gender")
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
@Column(name = "registerTime",columnDefinition = "timestamp default CURRENT_TIMESTAMP")
public Timestamp getRegisterTime() {
return registerTime;
}
public void setRegisterTime(Timestamp registerTime) {
this.registerTime = registerTime;
}
@Column(name = "loginTime",insertable = false)
public Timestamp getLoginTime() {
return loginTime;
}
public void setLoginTime(Timestamp loginTime) {
this.loginTime = loginTime;
}
@OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER,mappedBy="sender")
public Set<ChatRecord> getChatRecords() {
return chatRecords;
}
public void setChatRecords(Set<ChatRecord> chatRecords) {
this.chatRecords = chatRecords;
}
public User() {
}
public User(String name, String avatar, int sex) {
this.name = name;
this.avatar = avatar;
this.sex = sex;
}
}
同理,在ChatRecord类中我们也这样设计:
- @DynamicInsert 动态插入数据
- @ManyToOne(cascade = CascadeType.ALL) 多对一映射
- @JoinColumn(name=”senderId”) 设置外键(数据库将显示senderId这一列,但是在通过hibernate查询的时候会查询出整个sender对象)
package cn.zipple.entity;
import org.hibernate.annotations.DynamicInsert;
import javax.persistence.*;
import java.sql.Timestamp;
/**
* Created by zipple on 2017/11/13.
* 聊天记录
*/
@Entity
@Table(name = "chatRecord")
@DynamicInsert
public class ChatRecord {
private int id;
private User sender;
private int receiver;//指定id
private String content;
private Timestamp time;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
//不需要懒加载
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="senderId")
public User getSender() {
return sender;
}
public void setSender(User sender) {
this.sender = sender;
}
@Column(name = "receiverId")
public int getReceiver() {
return receiver;
}
public void setReceiver(int receiver) {
this.receiver = receiver;
}
@Column(name = "content",columnDefinition = "LONGTEXT")
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Column(name = "time",columnDefinition = "timestamp default CURRENT_TIMESTAMP")
public Timestamp getTime() {
return time;
}
public void setTime(Timestamp time) {
this.time = time;
}
public ChatRecord() {
}
public ChatRecord(User sender, int receiver, String content) {
this.sender = sender;
this.receiver = receiver;
this.content = content;
this.time=new Timestamp(System.currentTimeMillis());
}
}
由于我们在第一篇文章中添加hibernate.cfg.xml的时候直接指定了数据库为chatroom,为此我们必须保证mysql中有这个数据库。
所以我们在mysql命令行中使用这句命令创建数据库并且要防止中文乱码,注意事项传送门:
CREATE DATABASE IF NOT EXISTS chatroom DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
好的,后面的表什么的就不用我们自己创建了,hibernate在运行时会自动创建。
最后还有一步,我们需要在hibernate.cfg.xml中注册这两个实体类,如下图
编写DAO层方法
使用Hibernate的方法也特别简单,先加载hibernate.cfg.xml这个文件,然后获取SessionFactory获取session,建立事务,进行CRUD,再提交事务即可。详情请点击
我们在util包中建立HibernateUtil类帮助我们获取session
package cn.zipple.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
/**
* Created by zipple on 2017/10/12.
* hibernate 工具类
*/
public class HibernateUtil {
/**
* 连接数据库
* @return 返回session
*/
public static Session getHibernateSession(){
StandardServiceRegistry serviceRegistry=new StandardServiceRegistryBuilder().configure().build();
SessionFactory sessionFactory=new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory();
return sessionFactory.openSession();
}
}
在dao包里面,添加HibernateDao作为基类,以方便代码复用。
package cn.zipple.dao;
import cn.zipple.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import java.util.List;
/**
* Created by zipple on 2017/10/12.
* 父类提供基本的公共操作
*/
public class HibernateDao {
/**
* 增加一条记录
* @param object 持久化类
*/
public void save(Object object){
Session session = HibernateUtil.getHibernateSession();
Transaction tr =session.beginTransaction();
// session.save(object);
session.saveOrUpdate(object);
tr.commit();
session.close();
}
/**
* 删除记录
* @param object 持久化类
*/
public void del(Object object){
Session session = HibernateUtil.getHibernateSession();
Transaction tr =session.beginTransaction();
session.delete(object);
tr.commit();
session.close();
}
/**
* 修改对象信息
* @param object 持久化对象
*/
public void upd(Object object){
Session session = HibernateUtil.getHibernateSession();
Transaction tr =session.beginTransaction();
session.merge(object);//使用merge防止从数据库中查询出来的持久化对象再被查询一次,从而导致异
// 常:illegally attempted to associate a proxy with two open Sessions
//向修改过后的对象 合并
//session.saveOrUpdate(stu);
tr.commit();
session.close();
}
/**
* 获取数据库中所有对象
* @param extra 额外条件--附加条件
* @return 返回对象列表
*/
public List getAllObject(String entityClass,String extra){
Session session = HibernateUtil.getHibernateSession();
String hql = "from "+entityClass+extra;
System.out.println("hql:"+hql);
List studentList = session.createQuery(hql).list();
session.close();
return studentList;
}
/**
* 根据id获取对象
* @param id id
* @return 返回对象
*/
public Object getObjetcById(int id){
Session session = HibernateUtil.getHibernateSession();
Transaction tr =session.beginTransaction();
session.close();
Object obj = session.get(Object.class, id);
tr.commit();
session.close();
return obj;
}
}
同时,我们为这两个实体类建立接口,如下图所示
BaseDaoImpl类实际上只是单纯的继承了HibernateDao这个基类,为什么要建立这个BaseDaoImpl呢,实际上是为了便于对父类进行补充和修改。如下:
package cn.zipple.dao.impl;
import cn.zipple.dao.HibernateDao;
/**
* Created by zipple on 2017/11/13.
* 基类
*/
public class BaseDaoImpl extends HibernateDao {
}
在ChatRecordDao接口中,没有其他的什么需求需要自定义数据库操作方法,使用常规的CRUD方法即可。于是这个接口这样定义:
package cn.zipple.dao;
/**
* Created by zipple on 2017/11/13.
*/
public interface ChatRecordDao {
}
在impl包中需要实现这个接口,虽然是个空的:
package cn.zipple.dao.impl;
import cn.zipple.dao.ChatRecordDao;
/**
* Created by zipple on 2017/11/18.
*/
public class ChatRecordDaoImpl extends BaseDaoImpl implements ChatRecordDao {
}
但是在UserDao中,我们要补充一个方法,用于用户注册和查询
package cn.zipple.dao;
import cn.zipple.entity.User;
/**
* Created by zipple on 2017/11/13.
*/
public interface UserDao {
/**
* 根据用户名获取用户对象
* @param username 用户名
* @return 返回对象
*/
User getUserByUsername(String username);
}
在impl包中这样实现接口
package cn.zipple.dao.impl;
import cn.zipple.dao.UserDao;
import cn.zipple.entity.User;
import cn.zipple.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.query.Query;
import java.util.List;
/**
* Created by zipple on 2017/11/13.
*/
public class UserDaoImpl extends BaseDaoImpl implements UserDao {
@Override
public User getUserByUsername(String username) {
Session session = HibernateUtil.getHibernateSession();
String hql = "from User where name =:username";//User代表的是User实体类而不是表名
Query query = session.createQuery(hql);
query.setParameter("username",username);
System.out.println("hql:"+hql);
List userList = query.list();
session.close();
if (userList.size()!=0){
return (User) userList.get(0);
}
return null;
}
}
上面的代码中我们使用到了hql,是Hibernate方便我们自定义sql操作而定义的。
下面我们测试HIbernate是否正常工作。
Intellij连接数据库
首先我们检查IDE右侧边栏是否有database一项,如图
有的话就双击
没有的话双击shift打开database界面
选择mysql连接
连接
第一次连接可能没有驱动包,让IDE自动帮你下载即可。
连接成功以后
我们在test包中新建测试类
package cn.zipple.test;
import cn.zipple.dao.impl.BaseDaoImpl;
import cn.zipple.entity.ChatRecord;
import cn.zipple.entity.User;
import cn.zipple.util.Constant;
/**
* Created by zipple on 2017/11/13.
* 测试
*/
public class UserTest {
private BaseDaoImpl baseDao = new BaseDaoImpl();
public static void main(String[] args) {
System.out.println("测试hibernate是否正常工作");
new UserTest().testAdd();
}
private void testAdd(){
User user1 = new User("邹博","01.jpg", Constant.MALE);
User user2 = new User("李欣雨","02.jpg", Constant.FEMALE);
System.out.println("储存用户1");
baseDao.save(user1);//先保存没有维护关系的那一方
System.out.println("储存用户2");
baseDao.save(user2);
ChatRecord temp = new ChatRecord(user1,user2.getId(),"这是第一条测试信息");
System.out.println("储存聊天信息");
baseDao.save(temp);
}
}
测试成功运行