环境准备:
SpringBoot、JPA相关依赖,lombok插件及依赖、easy-poi依赖
数据库实体类:
1.User类:
- 注解@Excel 用于easy-poi声明表格中的列
- User和Order是一对多的关系,级联类型设置为所有,@orphanRemoval = true 表示删除某条User数据时会删除Order中对应user_id的数据
@Entity
@Data
@Table(name = "user", schema = "mydb", catalog = "")
public class User implements Serializable {
@Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Excel(name = "用户id", orderNum = "0", width = 15)
private Integer id;
@Basic
@Column(name = "username", nullable = true, length = 255)
@Excel(name = "用户名", orderNum = "0", width = 15)
private String username;
@Basic
@Column(name = "password", nullable = true, length = 255)
@Excel(name = "密码", orderNum = "0", width = 15)
private String password;
@OneToMany(cascade={CascadeType.ALL},orphanRemoval = true)
@JoinColumn(name="user_id")
@Transient
private List<Order> orderList;
}
2:Order类:
@Entity
@Data
@Table(name = "order", schema = "mydb", catalog = "")
public class Order implements Serializable {
@Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Basic
@Column(name = "user_id", nullable = true, length = 255)
private Integer userId;
@Basic
@Column(name = "order_time", nullable = true, length = 255)
private Date orderTime;
}
包装类Vo / DTO:
Date必须是java.util.Date,使用java.sql.Date报错:找不到对应构造方法(未找到原因,数据库字段类型为datetime)
- @NoArgsConstructor:无参构造器
- @AllArgsConstructor:所有属性的构造器(注意顺序)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserVo implements Serializable {
private Integer id;
private String username;
private String password;
// 添加的属性
private Date orderTime;
}
方法一:使用HQL直接返回Page< UserVo>
Dao接口:
- 注意构造器的参数顺序要一致,使用HQL查询时均使用实体类和其对应属性名
- 此处分页使用Pageable,直接作为入参即可。不需要分页时可直接返回List< UserVo >
@Repository
public interface UserDao extends JpaRepository<User, Integer> {
@Query(value = "select new com.stee.customermanagement.vo.UserVo(a.id,a.username,a.password,b.orderTime) " +
"from User a join Order b on a.id = b.userId and b.orderTime like concat('%',?1,'%')"
,countQuery = "select count(1) from from User a join Order b on a.id = b.userId and b.orderTime like concat('%',?1,'%')")
Page<UserVo> findAllByHQL(Date orderTime, Pageable pageable);
}
Service:
省略Serive接口、ListResp返回类
@Service
@Slf4j
public class UserServiceImpl {
@Autowired
UserDao userDao;
@Override
public ListResp findAllByHQL(Date orderTime , Pageable pageable) {
ListResp listResp = new ListResp();
try {
Page<UserVo> userVoPage = userDao.findAllByHQL(orderTime,pageable);
listResp.setData(userVoPage.getContent());
listResp.setCount(userVoPage.getTotalElements());
listResp.setCode(ResCode.R10003001.getCode());
} catch (Exception e){
log.error("the error of findAllByHQL() : ", e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
listResp.setCode(ResCode.R30103001.getCode());
}
return listResp;
}
}
方法二:使用原生SQL返回List<Object[]>
Date必须是java.util.Date,使用java.sql.Date报错:找不到对应构造方法(数据库字段类型为datetime)
- nativeQuery = true 表示开启原生SQL查询
- 查询字段别名需要与实体类中字段一一对应
- 此处分页使用limit操作。也可添加 ORDER BY ?#{#pageable} 配合Pageable完成分页,使用Page< Object[ ]>接收返回数据
Dao接口:
@Repository
public interface UserDao extends JpaRepository<User, Integer> {
@Query(value = "select a.id as id, a.username as username , a.password as password , b.order_time as b.orderTime " +
" from user a, order b where a.id = b.user_id and b.order_time like concat('%',?1,'%') limit ?2,?3"
,nativeQuery = true)
List<Object[]> findAll(Date orderTime, Integer startIndex , Integer perSize);
}
Service:
省略Serive接口
- 转换成dto的方法一:将objects中的所有参数强转为对应类型,传递到dto的构造器中;dto对象定义好对应的构造器
- 方法二:通过反射的方法简化dto的转化步骤
@Service
@Slf4j
public class UserServiceImpl {
@Autowired
UserDao userDao;
@Override
public ListResp findAll(Date orderTime,Pageable pageable) {
ListResp listResp = new ListResp();
try {
Integer startIndex = pageable.getPageNumber()*pageable.getPageSize();
List<Object[]> objects = userDao.findAll(orderTime,startIndex,pageable.getPageSize());
// 省略用于分页的count数据总条数
List<UserVo> voList = new ArrayList<>();
for(Object[] obj : objects){
UserVo vo = new UserVo((Integer) obj[0],(String)obj[1],(String)obj[2],(Date)obj[3]);
voList.add(vo);
}
listResp.setData(voList);
} catch (Exception e){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return listResp;
}
}
转换工具类
简单说明第二种方式的转换流程:
- 获取要转换的类型的全部构造器,将构造器集合进行遍历,与List<Object[]>中第一项object[]的长度比对,若构造器的参数数量与object[]数组的元素数量相同,则使用目标类的这个构造器进行实例化newInstance(obj)
- 此处要注意目标类要有对应的构造方法,尤其注意当目标类存在多个重载构造方法且参数数量相同时,在判断具体使用哪个构造方法时需要进行更改
public class JpaUtil<T> {
public static Object arrayTransferToObject(Object target , Object[] array) throws IllegalAccessException, InstantiationException {
Class clazz = target.getClass();
int i=0;
for (Field field : clazz.getDeclaredFields()){
field.setAccessible(true);
field.set(target, array[i]);
i++;
if(i == array.length){
break;
}
}
return target;
}
public static <T> List<T> arrayTransferToObject(List<Object[]> objList, Class<T> targetClz) throws Exception{
if(objList==null || objList.size()==0) {
return null;
}
Class<?>[] cz = null;
Constructor<?>[] cons = targetClz.getConstructors(); // 以数组形式获取构造器的参数集合
for(Constructor<?> ct : cons) {
Class<?>[] clazz = ct.getParameterTypes();
if(objList.get(0).length == clazz.length) {
cz = clazz;
break;
}
}
List<T> list = new ArrayList<T>();
for(Object[] obj : objList) {
Constructor<T> cr = targetClz.getConstructor(cz);
list.add(cr.newInstance(obj));
}
return list;
}
}