前言
本文需要一定的java8特性基础,共涉及到了默认方法,方法引用,迭代集合(测试),函数式接口的设计。
另外,本文是对Hibernate的常见单表操作进行了练习,包含了增删改,模糊查询,部分字段查询,分页查询,多条件查询,分页+条件查询等。
项目下载:见博主的下载资源。hibernate增删改查和分页+java8
项目环境
项目是一个动态web项目,但是没有添加页面。使用了MVC架构,从dao层写到了service层。
中间过程设计了几个接口,用于支持service提交事务。
目录结构如图所示,其中test包主要是为了测试service中的方法。
hibernate.cfg.xml在src目录下。
org.feng.service.support包中设计了几个接口,用于支持service事务提交(原因在于,只使用Hibernate时,事务提交代码固定,因此设计函数式接口进行优化,去除冗余代码)。下图是src中的所有内容:
完整项目
数据表
数据库名是toursim_system,表名是place。
数据表中,id是主键,自增。
org.feng.util
package org.feng.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static SessionFactory sf;
static {
// 1 创建,调用空参构造
Configuration conf = new Configuration().configure();
// 2 根据配置信息,创建 SessionFactory对象
sf = conf.buildSessionFactory();
}
/**
* 获取当前Session
* @return
*/
public static Session currentSession(){
// 3 获得session
return sf.getCurrentSession();
}
}
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--
#hibernate.dialect org.hibernate.dialect.MySQLDialect
#hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect
#hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect
#hibernate.connection.driver_class com.mysql.jdbc.Driver
#hibernate.connection.url jdbc:mysql:///test
#hibernate.connection.username gavin
#hibernate.connection.password
-->
<!-- 数据库驱动 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 数据库url -->
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/tourism_system</property>
<!-- 数据库连接用户名 -->
<property name="hibernate.connection.username">root</property>
<!-- 数据库连接密码 -->
<property name="hibernate.connection.password">root</property>
<!-- 数据库方言
不同的数据库中,sql语法略有区别. 指定方言可以让hibernate框架在生成sql语句时.针对数据库的方言生成.
sql99标准: DDL 定义语言 库表的增删改查
DCL 控制语言 事务 权限
DML 操纵语言 增删改查
注意: MYSQL在选择方言时,请选择最短的方言.
-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- #hibernate.show_sql true
#hibernate.format_sql true
-->
<!-- 将hibernate生成的sql语句打印到控制台 -->
<property name="hibernate.show_sql">true</property>
<!-- 将hibernate生成的sql语句格式化(语法缩进) -->
<property name="hibernate.format_sql">true</property>
<!--
## auto schema export 自动导出表结构. 自动建表
#hibernate.hbm2ddl.auto create 自动建表.每次框架运行都会创建新的表.以前表将会被覆盖,表数据会丢失.(开发环境中测试使用)
#hibernate.hbm2ddl.auto create-drop 自动建表.每次框架运行结束都会将所有表删除.(开发环境中测试使用)
#hibernate.hbm2ddl.auto update(推荐使用) 自动生成表.如果已经存在不会再生成.如果表有变动.自动更新表(不会删除任何数据).
#hibernate.hbm2ddl.auto validate 校验.不自动生成表.每次启动会校验数据库中表是否正确.校验失败.
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="current_session_context_class">thread</property>
<mapping class="org.feng.entity.Place"/>
</session-factory>
</hibernate-configuration>
org.feng.entity
实体类中使用注解与表映射:表名映射,id自增,其他各列映射。
package org.feng.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="place")
public class Place implements Serializable {
/**
* Feng2019年10月11日
*/
private static final long serialVersionUID = -5980494330051306592L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
@Column(name="cost")
private Integer cost;
@Column(name="city")
private String city;
@Column(name="description")
private String description;
@Column(name="picture")
private String picture;
public Place() {
}
public Place(Integer id) {
this.id = id;
}
// 该构造器用于查询语句HQL的实现
public Place(String name, Integer cost, String description) {
this(null, name, cost, null, description, null);
}
public Place(Integer id, String name, Integer cost, String city, String description, String picture) {
this.id = id;
this.name = name;
this.cost = cost;
this.city = city;
this.description = description;
this.picture = picture;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getCost() {
return cost;
}
public void setCost(Integer cost) {
this.cost = cost;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPicture() {
return picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
@Override
public String toString() {
return "Place [id=" + id + ", name=" + name + ", cost=" + cost + ", city=" + city + ", description="
+ description + ", picture=" + picture + "]";
}
}
org.feng.dao
package org.feng.dao;
import java.util.List;
import java.util.Map;
import org.feng.entity.Place;
/**
* 景点信息操作:数据访问接口
* @author Feng
* 2019年10月11日下午2:40:11
*/
public interface PlaceDao {
/**
* 分页
* @param pageNo
* @param pageSize
* @return
*/
public List<Place> getByPage(int pageNo, int pageSize);
/**
* 分页+条件查询
* @param pageNo
* @param pageSize
* @param place
* @return
*/
public List<Place> getByPage(int pageNo, int pageSize, Place place);
/**
* 获取前n条数据
* @param n
* @return
*/
public List<Place> getTopN(int n);
/**
* 获取表中的部分数据:name列、cost列、description列
* @return
*/
public List<Place> getSubInfo();
/**
* 查询表,通过Hibernate封装为List
* @return
*/
public List<Map<String, Object>> getMapList();
/**
* 通过id查询到Place信息
* @param id
* @return
*/
public Place getById(String id);
/**
* 通过景点名查找某一景点
* @param place
* @return
*/
public Place getByName(Place place);
/**
* 结果集
* @param place
* @return
*/
public List<Place> placeList();
/**
* 返回通过景点名查询的结果
* @param name
* @return
*/
public List<Place> getPlaceByNameLike(String name);
/**
* 返回通过城市名查询的结果
* @param city
* @return
*/
public List<Place> getPlaceByCityLike(String city);
/**
* 获取所有景点名
* @return
*/
public List<String> getPlaceName();
/**
* 插入数据
* @param type 任意一个实体类
* @return
*/
void insert(Place type);
/**
* 修改数据
* @param type 任意一个实体类
*/
void update(Place type);
/**
* 删除方法:通过id删除
* 如果id为int值,拼空字符串;
* 当int id = 1; -> 1+""
* @param id
* @return 数据库中影响的行数
*/
void delete(String id);
}
org.feng.dao.impl
继承了HibernateUtil工具类获取session,实现了PlaceDao接口,实现其中的抽象方法。
package org.feng.dao.impl;
import java.util.List;
import java.util.Map;
import org.feng.dao.PlaceDao;
import org.feng.entity.Place;
import org.feng.util.HibernateUtil;
public class PlaceDaoImpl extends HibernateUtil implements PlaceDao {
@SuppressWarnings("unchecked")
@Override
public List<Place> placeList(){
return currentSession()
.createQuery("from Place")
.list();
}
@SuppressWarnings("unchecked")
@Override
public List<Place> getPlaceByNameLike(String name) {
return currentSession()
.createQuery("from Place where name like ?")
.setString(0, "%"+name+"%")
.list();
}
@SuppressWarnings("unchecked")
@Override
public List<Place> getPlaceByCityLike(String city) {
return currentSession()
.createQuery("from Place where city like ?")
.setString(0, "%"+city+"%")
.list();
}
@SuppressWarnings("unchecked")
@Override
public List<String> getPlaceName() {
return currentSession()
.createQuery("select name from Place")
.list();
}
@Override
public void insert(Place type) {
currentSession().save(type);
}
@Override
public void update(Place type) {
currentSession().update(type);
}
@Override
public void delete(String id) {
currentSession().delete(new Place(Integer.valueOf(id)));
}
@Override
public Place getById(String id) {
return (Place) currentSession()
.createQuery("from Place where id = ?")
.setInteger(0, Integer.valueOf(id))
.uniqueResult();
}
@Override
public Place getByName(Place place) {
return (Place) currentSession()
.createQuery("from Place place where place.name = :name")
.setString("name", place.getName())
.uniqueResult();
}
@SuppressWarnings("unchecked")
@Override
public List<Place> getSubInfo() {
return currentSession()
.createQuery("select new Place(name, cost, description) from Place")
.list();
}
@SuppressWarnings("unchecked")
@Override
public List<Map<String, Object>> getMapList() {
return currentSession()
.createQuery("select new Map(name as placeNmae,description as info) from Place")
.list();
}
@SuppressWarnings("unchecked")
@Override
public List<Place> getByPage(int pageNo, int pageSize) {
return currentSession()
.createQuery("from Place")
.setFirstResult((pageNo - 1) * pageSize)
.setMaxResults(pageSize)
.list();
}
@SuppressWarnings("unchecked")
@Override
public List<Place> getTopN(int n) {
return currentSession()
.createQuery("from Place")
.setMaxResults(n)
.list();
}
@SuppressWarnings("unchecked")
@Override
public List<Place> getByPage(int pageNo, int pageSize, Place place) {
return currentSession()
.createQuery("from Place place where place.name like ?")
.setFirstResult((pageNo - 1) * pageSize)
.setMaxResults(pageSize)
.setString(0, "%"+place.getName()+"%")
.list();
}
}
org.feng.service
service层的方法与Dao层的方法一致。
特殊在于,这里的service接口继承了ServiceSupport接口,该接口中写了3个默认的方法,用于查询、修改、增加、删除、条件查询。
package org.feng.service;
import java.util.List;
import java.util.Map;
import org.feng.entity.Place;
import org.feng.service.support.ServiceSupport;
public interface PlaceService extends ServiceSupport{
/**
* 获取表中的部分数据:name列、cost列、description列
* @return
*/
public List<Place> getSubInfo();
/**
* 结果集
* @param place
* @return
*/
public List<Place> placeList();
/**
* 返回通过景点名查询的结果
* @param name
* @return
*/
public List<Place> getPlaceByNameLike(String name);
/**
* 返回通过城市名查询的结果
* @param city
* @return
*/
public List<Place> getPlaceByCityLike(String city);
public Place getByName(Place place);
/**
* 查找部分信息,放到Map中
* @return
*/
public List<Map<String, Object>> getMapList();
/**
* 获取所有景点名
* @return
*/
public List<String> getPlaceName();
public Place getById(String id);
void insert(Place type);
void update(Place type);
void delete(String id);
public List<Place> getByPage(int pageNo, int pageSize);
public List<Place> getByPage(int pageNo, int pageSize, Place place);
public List<Place> getTopN(int n);
}
org.feng.service.support
自定义函数式接口。
package org.feng.service.support;
import org.feng.util.HibernateUtil;
import org.hibernate.Transaction;
/**
* 查询支持接口:提供默认的事务操作处理。返回值为java.util.List等
* @author Feng
* 2019年10月10日下午4:41:02
*/
@FunctionalInterface
public interface QuerySupport<T> {
/**
* 查询专用:当查询无参数注入时(查询所有,无条件);
* 当查询有参数注入(有条件查询),也不影响,参数直接从service层进入dao层的方法。
* @param <T> 某一类型,可以是entity类型、String、List等
* @return
*/
default T doQuery() {
Transaction tx = null;
T result = null;
try {
tx = HibernateUtil.currentSession().beginTransaction();
result = execute();
tx.commit();
} catch (Exception e) {
if(tx != null) {
tx.rollback();
}
}
return result;
}
/**
* 查询操作
* @param <T>
* @return
*/
T execute();
}
package org.feng.service.support;
import org.feng.util.HibernateUtil;
import org.hibernate.Transaction;
/**
* 插入、修改删除操作的接口
* @author Feng
* 2019年10月10日下午4:44:24
*/
@FunctionalInterface
public interface SaveOrUpdateOrDeleteSupport {
/**
* 封装:事务提交和回滚,主要是用于增加、删除、修改操作;
*/
default void doSomeThing() {
Transaction tx = null;
try {
tx = HibernateUtil.currentSession().beginTransaction();
execute();
tx.commit();
} catch (Exception e) {
if(tx != null) {
tx.rollback();
}
}
}
/**
* 插入、修改、删除
*/
void execute();
}
package org.feng.service.support;
import org.feng.util.HibernateUtil;
import org.hibernate.Transaction;
/**
* 复杂查询,有条件查询
* @author Feng
* 2019年10月11日下午2:21:36
* @param <T> 条件(sql的参数)
* @param <R> 结果
*/
@FunctionalInterface
public interface ComplexQuerySupport<T, R> {
R complexQuery(T t);
default R doQuery(T t) {
Transaction tx = null;
R result = null;
try {
tx = HibernateUtil.currentSession().beginTransaction();
result = complexQuery(t);
tx.commit();
} catch (Exception e) {
if(tx != null) {
tx.rollback();
}
}
return result;
}
}
package org.feng.service.support;
public interface ServiceSupport {
/**
* 面向接口:查询
* @param <T>
* @param support
* @return
*/
default <T> T doQuery(QuerySupport<T> support){
return support.doQuery();
}
/**
* 面向接口操作:增加、删除、修改
* @param support
*/
default void doSomeThing(SaveOrUpdateOrDeleteSupport support) {
support.doSomeThing();
}
/**
* 复杂查询:有条件查询
* @param support
* @param argument
* @return
*/
default <R, T> R doComplexQuery(ComplexQuerySupport<T, R> support, T argument) {
return support.doQuery(argument);
}
}
org.feng.service.impl
service实现类,其中可以调用PlaceService接口继承得来的3个默认方法,分别用于查询、增加、修改、删除等的事务提交操作。也就是说,在本层中不需要显示写出事务提交部分的代码,因为这些操作已经在对应的***Support.java接口文件中做过了。
package org.feng.service.impl;
import java.util.List;
import java.util.Map;
import org.feng.dao.PlaceDao;
import org.feng.dao.impl.PlaceDaoImpl;
import org.feng.entity.Place;
import org.feng.service.PlaceService;
public class PlaceServiceImpl implements PlaceService {
private PlaceDao dao;
public PlaceServiceImpl() {
dao = new PlaceDaoImpl();
}
/**
* 使用java8的方法引用:dao::placeList
* 意思是,dao调用placeList的方法,并返回。
*/
@Override
public List<Place> placeList() {
return doQuery(dao::placeList);
}
@Override
public List<Place> getPlaceByNameLike(String name) {
return doComplexQuery(dao::getPlaceByNameLike, name);
}
@Override
public List<Place> getPlaceByCityLike(String city) {
return doComplexQuery(dao::getPlaceByCityLike, city);
}
@Override
public List<String> getPlaceName() {
return dao.getPlaceName();
}
@Override
public void insert(Place type) {
doSomeThing(() -> dao.insert(type));
}
@Override
public void update(Place type) {
doSomeThing(() -> dao.update(type));
}
@Override
public void delete(String id) {
doSomeThing(() -> dao.delete(id));
}
@Override
public Place getById(String id) {
return doComplexQuery(dao::getById, id);
}
@Override
public Place getByName(Place place) {
return doComplexQuery(dao::getByName, place);
}
@Override
public List<Place> getSubInfo() {
return doQuery(dao::getSubInfo);
}
@Override
public List<Map<String, Object>> getMapList() {
return doQuery(dao::getMapList);
}
@Override
public List<Place> getByPage(int pageNo, int pageSize) {
return doQuery(()->{
return dao.getByPage(pageNo, pageSize);
});
}
@Override
public List<Place> getTopN(int n) {
return doComplexQuery(dao::getTopN, n);
}
@Override
public List<Place> getByPage(int pageNo, int pageSize, Place place) {
return doQuery(()->{
return dao.getByPage(pageNo, pageSize, place);
});
}
}
org.feng.service.impl.test
测试使用了junit测试。
经过测试,目标方法均可正常执行。
package org.feng.service.impl.test;
import org.feng.entity.Place;
import org.feng.service.PlaceService;
import org.feng.service.impl.PlaceServiceImpl;
import org.junit.Test;
public class ServiceTest {
PlaceService service = new PlaceServiceImpl();
@Test
public void insert() {
service.insert(new Place(0, "西湖", 0, "杭州", "水很好", ""));
}
@Test
public void update() {
service.update(new Place(20, "西湖", 0, "杭州", "水很好", "beau.png"));
}
@Test
public void query() throws Exception {
service.placeList().forEach(System.out::println);
}
@Test
public void testLike() {
service.getPlaceByNameLike("大").forEach(System.out::println);
}
@Test
public void testId() {
System.out.println(service.getById("1"));
}
@Test
public void testPlaceByName() {
Place place = new Place();
place.setName("大雁塔");
System.out.println(service.getByName(place));
}
@Test
public void testSubInfo() {
service.getSubInfo().forEach(System.out::println);
}
@Test
public void testMap() {
service.getMapList().forEach(System.out::println);
}
@Test
public void page() {
System.out.println("第1页:");
service.getByPage(1, 2).forEach(System.out::println);
System.out.println("第2页:");
service.getByPage(2, 2).forEach(System.out::println);
}
@Test
public void complexPage() {
System.out.println("第1页:");
Place place = new Place();
place.setName("大");
service.getByPage(1, 1, place).forEach(System.out::println);
System.out.println("第2页:");
service.getByPage(2, 1, place).forEach(System.out::println);
}
}