hibernate 映射相关问题

需求背景

项目需求,将中间数据库的数据查询到本地视图,再将本地视图(千万级数据量)和会员表(百万级数据量)关联查询导出相关数据,
因为数据量比较大,一次全部查出或导致内存溢出,因此,后台需要对这些关联数据分页查询然后分批导出cvs文件,然后上传到媒体服务器。


需求实现过程中处理的问题

1.视图处理须知:将视图注解(配置)实体关系映射,并将添加的实体加入 persistence.xml 中;因为 查询实现的方式是 EJB JPA 实体管理器 创建的query,PersistenceContext(持久化上下文)上下文的配置文件中的 持久化单元 中配置的相关实体,否则会 出现 为 sql 语句中的变量赋值时,无法将传入的参数设置到sql语句中

1
java.lang.IndexOutOfBoundsException: Remember that ordinal parameters are 1-based!

这个问题产生的原因是 hibernate 通过 持久化上下文 中查找不到 相关实体,无法映射表结构,不能将参数设置到sql语句中,提示出这种很疑惑的问题,反复调试,能看到传入的变量值和创建出来的sql语句和sql变参,可就是无法将变量值设置给sql变参,后台日志输出上面的提示,很费解。

2.创建视图实体和联合主键:使用注解方式实现,参照 Hibernate怎样使用Annotation映射视图Annotation对Hibernate中联合主键定义 将视图所有的字段放在联合主键实体中,该实体用

1
@Embeddable

注解,字段和普通实体一样使用

1
@Column

注解,在视图实体中使用该联合主键实体,用

1
@EmbeddedId @AttributeOverrides({...})

注解,这样视图映射关系已经搞定,因为开启了org.hibernate.tool.hbm2ddl.SchemaUpdate - Running hbm2ddl schema update,在启动过程中会报

1
2
ERROR com.huanyu.common.jpa.index.OracleDbIndexBuilder - 创建“View_memcon”表主键(...)出错。
java.sql.SQLException: ORA-00942: 表或视图不存在

处理这个报错的方法是,关闭 hibernate hbm2ddl = none 即可,不需要更新索引,这个报错并不影响功能,只是在容器启动时报这么个错误看着不舒服,因为 本来 视图 就没有 主键 和 索引,这样去为视图 创建实体映射关系并添加联合主键,只是为了能使用 hibernate 去查询视图
3.关联查询语句: sql 语句 表、字段 is not mapped 问题
4.分批量导出多个文件: 因为写入单个文件的耗时线性增长,600万数据写入一个文件中,10多个小时没导完

视图处理

将视图映射成实体表,过程中 直接使用 em.createQuery(“数据库查询语句”) 报错:表 is not mapped 的问题,因此,引出了需要为视图创建实体和联合主键
创建视图实体和联合主键参照 Hibernate怎样使用Annotation映射视图 ,启动报关于视图 不存在的问题是因为开启了
更新表结构的配置,因此为视图更新表结构的时候创建联合主键时为视图更新索引报错,将 hibernate 的 hbm2ddl 配置关闭即可。

过程中,调试关于 java.lang.IndexOutOfBoundsException: Remember that ordinal parameters are 1-based! 的问题,反复调试总不知如何解决,后来得到指点
将添加的视图实体加入 persistence.xml 中 之后,这个问题得到解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2016-01-13 10:32:26,742 [common-quartz-core.scheduler_Worker-2] ERROR com.huanyu.mbr.core.consumption.dao.ConsumptionDao - search(definition=WHERE
code.EQUALS(1507070011)
ORDER BY
PAGE SIZE = 5000
PAGE = 0) error
SQL=SELECT v.*,m.name,m.sex,m.idNo,m.cell,m.cityName
FROM com.huanyu.mbr.core.consumption.dao.Consumption AS consumption , com.huanyu.mbr.core.consumption.dao.view_memcon AS v , com.huanyu.mbr.dao.mbr.Mbr AS m
WHERE (consumption.code in ?) AND (m.id=v.code) AND (v.code=consumption.queryCode)
java.lang.IndexOutOfBoundsException: Remember that ordinal parameters are 1-based!
at org.hibernate.engine.query.ParameterMetadata.getOrdinalParameterDescriptor(ParameterMetadata.java:55)
at org.hibernate.engine.query.ParameterMetadata.getOrdinalParameterExpectedType(ParameterMetadata.java:61)
at org.hibernate.impl.AbstractQueryImpl.determineType(AbstractQueryImpl.java:397)
at org.hibernate.impl.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:369)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:234)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:311)
at com.sun.proxy.$Proxy94.setParameter(Unknown Source)
...

这个问题 说明 对于 hibernate基础 掌握不扎实,对于 EJB JPA 概念理解不深

关联查询语句问题及处理(表、字段 not mapped )【hibernate 基础不扎实】

EJB JPA 实体管理器

1
2
@PersistenceContext(unitName = "huanyu-mbr-core.persistent-unit")
protected EntityManager em;

下述是问题,是因为 em.createQuery(“sql…”),其中 sql 中 直接使用了 数据库表名称(view_memcon),而不是 实体名称(Member)

对于 视图的字段属性 的 操作:select v.viewData.code from View_memcon v … 其中 View_memcon 是视图实体名称,v.viewData.code 是通过联合主键实体映射来映射视图字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
java.lang.IllegalArgumentException: org.hibernate.hql.ast.QuerySyntaxException: view_memcon is not mapped [SELECT COUNT(m.id) FROM com.huanyu.mbr.core.consumption.dao.view_memcon v, t_mbr m WHERE m.id = v.memId AND v.unionCode = (select p.queryCode from t_consumption p where p.code = :code)]
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:616)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:95)
at sun.reflect.GeneratedMethodAccessor62.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366)
at com.sun.proxy.$Proxy64.createQuery(Unknown Source)
at sun.reflect.GeneratedMethodAccessor62.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:241)
at com.sun.proxy.$Proxy64.createQuery(Unknown Source)
...
Caused by: org.hibernate.hql.ast.QuerySyntaxException: view_memcon is not mapped [SELECT COUNT(m.id) FROM com.huanyu.mbr.core.consumption.dao.view_memcon v, t_mbr m WHERE m.id = v.memId AND v.unionCode = (select p.queryCode from t_consumption p where p.code = :code)]
at org.hibernate.hql.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:158)
at org.hibernate.hql.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:87)
at org.hibernate.hql.ast.tree.FromClause.addFromElement(FromClause.java:71)
at org.hibernate.hql.ast.HqlSqlWalker.createFromElement(HqlSqlWalker.java:295)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3228)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3112)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:720)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:571)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:288)
at org.hibernate.hql.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:231)
at org.hibernate.hql.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:231)
at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:162)
at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:113)
at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:77)
at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:56)
at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:72)
at org.hibernate.impl.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:133)
at org.hibernate.impl.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:112)
at org.hibernate.impl.SessionImpl.createQuery(SessionImpl.java:1624)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:92)
... 105 more

分批量导出多个文件

由于导出数据量比较大,导出单个文件写入耗时性能降低,需要分批量导出多个文件,压缩上传到媒体服务器;google 到 How to compress files in ZIP format
使用 java.util.zip.ZipEntry 进行导出操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
AppCtxUtils  工具类

package com.huanyu.common.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
* @author ares
*
*/

public class AppCtxUtils implements ApplicationContextAware, DisposableBean {

private static ApplicationContext appCtx;

@Override
public void setApplicationContext(ApplicationContext appCtx1) throws BeansException {
appCtx = appCtx1;

}

public static ApplicationContext getAppCtx() {
assertContextInjected();
return appCtx;
}

public static <T> T getBean(Class<T> clazz) {
assertContextInjected();
return appCtx.getBean(clazz);
}

public static <T> T getBean(String beanName) {
assertContextInjected();
return (T) appCtx.getBean(beanName);
}

public static <T> T getBean(String beanName, Class<T> clazz) {
assertContextInjected();
return appCtx.getBean(beanName, clazz);
}

private static void assertContextInjected() {
if (appCtx == null) {
throw new IllegalStateException("applicaitonContext未注入!");
}
}

@Override
public void destroy() throws Exception {
appCtx = null;

}
}


文件 打包相关 方法

/**
* 创建临时文件夹目录(供存放多个临时导出文件)
*
* @return 创建的临时文件目录绝对路径
* @throws Exception
*/

private String getTempFileDirPath() throws Exception {
// 创建本地资源路径
String relativePath = SYSTEM_STATIC_FILE_PATH + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date())
+ "-" + UUID.randomUUID();
Resource resource = AppCtxUtils.getAppCtx().getResource(relativePath);
File file = resource.getFile();
if (!file.exists()) {
file.mkdirs();
}

String realPath = resource.getFile().getPath();

return realPath;

}

/**
* 文件打包
*
* @return zip文件路径
* @throws IOException
*/

private String zipFile() throws IOException {
String zipFilePath = this.template.getName() + "-"
+ new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + KEY_ZIPSUFFIX;
byte[] buf = new byte[1024];
FileOutputStream f = new FileOutputStream(zipFilePath);
ZipOutputStream out = new ZipOutputStream(f);

for (String filePath : this.fileList) {
File file = new File(filePath);

ZipEntry ze = new ZipEntry(file.getName());
out.putNextEntry(ze);

FileInputStream in = new FileInputStream(file);

int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}

in.close();
}
out.closeEntry();
out.close();

return zipFilePath;
}

上述问题的处理解决过程中,个人 hibernate 基础 有待深入学习


参考资料

EJB之JPA(EntityManager)

Hibernate怎样使用Annotation映射视图Annotation对Hibernate中联合主键定义

Hibernate table not mapped error

How to compress files in ZIP format