奇怪的延迟加载异常(Strange lazy loading exception)
我有一个基于Spring的webapp,我的问题是在我的代码发生变化之后,我开始得到延迟加载异常。 下面我详细描述一下情况:
在一开始的时候
我有一个帐户和Word实体。 一个帐户可以有多个单词,一个Word可以分配给多个帐户。
Account.class
@ManyToMany(targetEntity = Word.class, fetch = FetchType.LAZY) @JoinTable(name = "account_word", joinColumns = {@JoinColumn(name="account_id")}, inverseJoinColumns = {@JoinColumn(name="word_id")}) @OrderBy("word") private List<Word> words;
Word.class
@ManyToMany(targetEntity = Account.class, fetch = FetchType.LAZY, mappedBy = "words") @JsonIgnore private List<Account> accounts;
除了每个帐户只能有一个“WordForToday”,它由在Account.class中映射的Word实体表示,如下所示:
@OneToOne @JoinColumn(name="word_for_today") private Word wordForToday;
一切都运转正常。 特别是我有一个@Scheduled方法,每天调用一次以更改每个帐户的“WordForToday”:
WordServiceImpl.class
@Transactional @Service public class WordServiceImpl implements WordService { @Autowired AccountDao accountDao; @PersistenceContext EntityManager entityManager; @Override @Scheduled(cron="0 0 0 * * ?") public void setNewWordsForToday() { logger.info("Starting setting new Words For Today"); List<Account> allAccounts = accountDao.listAccounts(); for(Account account : allAccounts) { if(hasListAtLeastOneWordWithDefinitionWhichIsNotSetAsWordForToday(account.getWords(), account.getUsername())) { account.setWordForToday(getUserRandomWordWithDefinition(account.getUsername())); entityManager.persist(account); } } logger.info("Setting new Words For Today ended"); } @Override @Transactional public List<Word> listUserWords(String username) { try { Account foundAccount = accountDao.findUserByUsername(username); List<Word> userWords = foundAccount.getWords(); userWords.size(); return userWords; } catch (UserNotFoundException unf) { logger.error("User not found: " + username, unf.getMessage()); return Collections.emptyList(); } } }
AccountDaoImpl.class
@Override public Account findUserByUsername(String username) throws UserNotFoundException { CriteriaQuery<Account> c = cb.createQuery(Account.class); Root<Account> r = c.from(Account.class); try { c.select(r).where(cb.equal(r.get("username"), username)); Account foundAccount = entityManager.createQuery(c).getSingleResult(); return foundAccount; } catch(NoResultException nre){ throw new UserNotFoundException(); } } @Override public List<Account> listAccounts() { CriteriaQuery<Account> cq = cb.createQuery(Account.class); Root<Account> account = cq.from(Account.class); cq.select(account); TypedQuery<Account> q = entityManager.createQuery(cq); List<Account> accounts = q.getResultList(); return accounts; }
上面的代码没有延迟加载异常。 懒惰取得正确的词。
那么
我必须为每个帐户实现单词组,所以我在项目中添加了新的Group实体。 现在除了“WordForToday”没有改变之外,Account和Word之间没有直接关系。 现在一个帐户可以有多个组,并且只能将一个组分配给一个帐户[具有连接表的单向一对多]。
Account.class
@OneToMany(fetch = FetchType.LAZY) @JoinTable(name = "account_wordgroup", joinColumns = {@JoinColumn(name="account_id")}, inverseJoinColumns = {@JoinColumn(name="wordgroup_id")}) @OrderBy("name") private List<Group> groups;
另外,一个组可以有多个单词,一个单词可以分配给许多组。
Group.class
@ManyToMany(targetEntity = Word.class, fetch = FetchType.EAGER) @OrderBy(value="word") @JoinTable(name = "wordgroup_word", joinColumns = {@JoinColumn(name="wordgroup_id")}, inverseJoinColumns = {@JoinColumn(name="word_id")}) private List<Word> words;
Word.class
@ManyToMany(targetEntity = Group.class, fetch = FetchType.LAZY, mappedBy = "words") @JsonIgnore private List<Group> groups;
并且使用上述实体的每个CRUD方法都能正常工作。 我只对上面提到的setNewWordsForToday()方法有问题,现在看起来像这样(我重构了一些代码):
WordServiceImpl.class
@Transactional @Service public class WordServiceImpl implements WordService { @Autowired AccountDao accountDao; @PersistenceContext EntityManager entityManager; @Autowired GroupService groupService; @Override @Scheduled(cron="0 0 0 * * ?") @Transactional public void setNewWordsForToday() { logger.info("Starting setting new Words For Today"); List<Account> allAccounts = accountDao.listAccounts(); for(Account account : allAccounts) { if(hasListAtLeastOneWordWithDefinitionWhichIsNotSetAsWordForToday(listUserWords(account), account)) { account.setWordForToday(getUserRandomWordWithDefinition(account)); entityManager.persist(account); } } logger.info("Setting new Words For Today ended"); } @Override @Transactional public List<Word> listUserWords(Account account) { List<Group> userGroups = groupService.listUserGroups(account); List<Word> userWords = new ArrayList<Word>(); for(Group userGroup : userGroups) { userWords.addAll(userGroup.getWords()); } return userWords; } }
GroupServiceImpl.class
@Transactional @Service public class GroupServiceImpl implements GroupService { @Override @Transactional public List<Group> listUserGroups(Account account) { List<Group> userGroups = account.getGroups(); userGroups.size(); return userGroups; }
}
AccountDaoImpl.class没有改变。 现在,当调用@Scheduled方法时,我有这个延迟加载异常:
ERROR [org.springframework.scheduling.support.MethodInvokingRunnable] - Invocation of method 'setNewWordsForToday' on target class [class pl.net.grodek.snd.service.WordServiceImpl] failed org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: pl.net.grodek.snd.model.Account.groups, no session or session was closed at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:394) at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:386) at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:126) at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:242) at pl.net.grodek.snd.service.GroupServiceImpl.listUserGroups(GroupServiceImpl.java:63) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy48.listUserGroups(Unknown Source) at pl.net.grodek.snd.service.WordServiceImpl.listUserWords(WordServiceImpl.java:83) at pl.net.grodek.snd.service.WordServiceImpl.setNewWordsForToday(WordServiceImpl.java:333) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.util.MethodInvoker.invoke(MethodInvoker.java:273) at org.springframework.scheduling.support.MethodInvokingRunnable.run(MethodInvokingRunnable.java:65) at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:51) at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:98) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:206) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:662)
我想我尝试了一切,我不知道该怎么做。 它现在阻挡了我几天所以请任何人帮我这个:(
PS:我当然正在使用OpenEntityManagerInViewFilter。
I have a Spring-based webapp and my problem is after a change in my code I started to get lazy loading exception. Below I describe the situation in details:
In the beginning
I had an Account and Word entities. One account can have many words and one Word can be assigned to many Accounts.
Account.class
@ManyToMany(targetEntity = Word.class, fetch = FetchType.LAZY) @JoinTable(name = "account_word", joinColumns = {@JoinColumn(name="account_id")}, inverseJoinColumns = {@JoinColumn(name="word_id")}) @OrderBy("word") private List<Word> words;
Word.class
@ManyToMany(targetEntity = Account.class, fetch = FetchType.LAZY, mappedBy = "words") @JsonIgnore private List<Account> accounts;
Except that every Account can have only one "WordForToday" which was represented by Word entity mapped in Account.class like this:
@OneToOne @JoinColumn(name="word_for_today") private Word wordForToday;
Everything was working properly. In particular I had a @Scheduled method which was invoked once a day to change the "WordForToday" for every Account:
WordServiceImpl.class
@Transactional @Service public class WordServiceImpl implements WordService { @Autowired AccountDao accountDao; @PersistenceContext EntityManager entityManager; @Override @Scheduled(cron="0 0 0 * * ?") public void setNewWordsForToday() { logger.info("Starting setting new Words For Today"); List<Account> allAccounts = accountDao.listAccounts(); for(Account account : allAccounts) { if(hasListAtLeastOneWordWithDefinitionWhichIsNotSetAsWordForToday(account.getWords(), account.getUsername())) { account.setWordForToday(getUserRandomWordWithDefinition(account.getUsername())); entityManager.persist(account); } } logger.info("Setting new Words For Today ended"); } @Override @Transactional public List<Word> listUserWords(String username) { try { Account foundAccount = accountDao.findUserByUsername(username); List<Word> userWords = foundAccount.getWords(); userWords.size(); return userWords; } catch (UserNotFoundException unf) { logger.error("User not found: " + username, unf.getMessage()); return Collections.emptyList(); } } }
AccountDaoImpl.class
@Override public Account findUserByUsername(String username) throws UserNotFoundException { CriteriaQuery<Account> c = cb.createQuery(Account.class); Root<Account> r = c.from(Account.class); try { c.select(r).where(cb.equal(r.get("username"), username)); Account foundAccount = entityManager.createQuery(c).getSingleResult(); return foundAccount; } catch(NoResultException nre){ throw new UserNotFoundException(); } } @Override public List<Account> listAccounts() { CriteriaQuery<Account> cq = cb.createQuery(Account.class); Root<Account> account = cq.from(Account.class); cq.select(account); TypedQuery<Account> q = entityManager.createQuery(cq); List<Account> accounts = q.getResultList(); return accounts; }
And this code above was without lazy loading exception. Words where lazy fetched properly.
So then
I had to implement Groups of Words for every Account, so I added new Group entity in my project. Now there is no direct relationship between Account and Word except "WordForToday" which didn't changed. Now one Account can have many Groups and only one Group can be assigned to one Account [unidirectional one-to-many with join table].
Account.class
@OneToMany(fetch = FetchType.LAZY) @JoinTable(name = "account_wordgroup", joinColumns = {@JoinColumn(name="account_id")}, inverseJoinColumns = {@JoinColumn(name="wordgroup_id")}) @OrderBy("name") private List<Group> groups;
Additionally one Group can have many words and one Word can be assigned to many groups.
Group.class
@ManyToMany(targetEntity = Word.class, fetch = FetchType.EAGER) @OrderBy(value="word") @JoinTable(name = "wordgroup_word", joinColumns = {@JoinColumn(name="wordgroup_id")}, inverseJoinColumns = {@JoinColumn(name="word_id")}) private List<Word> words;
Word.class
@ManyToMany(targetEntity = Group.class, fetch = FetchType.LAZY, mappedBy = "words") @JsonIgnore private List<Group> groups;
And every CRUD methods which use this Entities above are working properly. I only have problem with setNewWordsForToday() method mentioned above which now looks like this (I refactored code a bit):
WordServiceImpl.class
@Transactional @Service public class WordServiceImpl implements WordService { @Autowired AccountDao accountDao; @PersistenceContext EntityManager entityManager; @Autowired GroupService groupService; @Override @Scheduled(cron="0 0 0 * * ?") @Transactional public void setNewWordsForToday() { logger.info("Starting setting new Words For Today"); List<Account> allAccounts = accountDao.listAccounts(); for(Account account : allAccounts) { if(hasListAtLeastOneWordWithDefinitionWhichIsNotSetAsWordForToday(listUserWords(account), account)) { account.setWordForToday(getUserRandomWordWithDefinition(account)); entityManager.persist(account); } } logger.info("Setting new Words For Today ended"); } @Override @Transactional public List<Word> listUserWords(Account account) { List<Group> userGroups = groupService.listUserGroups(account); List<Word> userWords = new ArrayList<Word>(); for(Group userGroup : userGroups) { userWords.addAll(userGroup.getWords()); } return userWords; } }
GroupServiceImpl.class
@Transactional @Service public class GroupServiceImpl implements GroupService { @Override @Transactional public List<Group> listUserGroups(Account account) { List<Group> userGroups = account.getGroups(); userGroups.size(); return userGroups; }
}
AccountDaoImpl.class didn't change. And Now i have this lazy loading exception when @Scheduled method is invoked:
ERROR [org.springframework.scheduling.support.MethodInvokingRunnable] - Invocation of method 'setNewWordsForToday' on target class [class pl.net.grodek.snd.service.WordServiceImpl] failed org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: pl.net.grodek.snd.model.Account.groups, no session or session was closed at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:394) at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:386) at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:126) at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:242) at pl.net.grodek.snd.service.GroupServiceImpl.listUserGroups(GroupServiceImpl.java:63) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy48.listUserGroups(Unknown Source) at pl.net.grodek.snd.service.WordServiceImpl.listUserWords(WordServiceImpl.java:83) at pl.net.grodek.snd.service.WordServiceImpl.setNewWordsForToday(WordServiceImpl.java:333) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.util.MethodInvoker.invoke(MethodInvoker.java:273) at org.springframework.scheduling.support.MethodInvokingRunnable.run(MethodInvokingRunnable.java:65) at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:51) at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:98) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:206) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:662)
I think that i tried everything and I have no more idea what to do. It is blocking me for a couple of days for now so please anyone help me with this :(
PS: I am using OpenEntityManagerInViewFilter of course.
原文:
最满意答案
做了一个小的改动,包括列名重复部分,请试试这个,
>>> from pyspark.sql.types import * >>>import re >>> l=[('val1','val2','val3'),('val4','val5','val6')] >>> l_schema = StructType([StructField("eng hours",StringType(),True),StructField("eng_hours",StringType(),True),StructField("test.apt",StringType(),True)]) >>> rdd = sc.parallelize(l) >>> df = sqlContext.createDataFrame(rdd,l_schema) >>> reps=('.','_'),(' ','_') >>> df.printSchema() root |-- eng hours: string (nullable = true) |-- eng_hours: string (nullable = true) |-- test.apt: string (nullable = true) >>> colnames = df.schema.names >>> def colrename(x): ... newcol = reduce(lambda a,kv : a.replace(*kv),reps,x) ... return re.sub('[. ]','',x) if newcol in colnames else newcol >>> for i in colnames: ... df = df.withColumnRenamed(i,colrename(i)) >>> df.printSchema() root |-- enghours: string (nullable = true) |-- eng_hours: string (nullable = true) |-- test_apt: string (nullable = true)
Did a small change to include column name duplicate part, please try this,
>>> from pyspark.sql.types import * >>>import re >>> l=[('val1','val2','val3'),('val4','val5','val6')] >>> l_schema = StructType([StructField("eng hours",StringType(),True),StructField("eng_hours",StringType(),True),StructField("test.apt",StringType(),True)]) >>> rdd = sc.parallelize(l) >>> df = sqlContext.createDataFrame(rdd,l_schema) >>> reps=('.','_'),(' ','_') >>> df.printSchema() root |-- eng hours: string (nullable = true) |-- eng_hours: string (nullable = true) |-- test.apt: string (nullable = true) >>> colnames = df.schema.names >>> def colrename(x): ... newcol = reduce(lambda a,kv : a.replace(*kv),reps,x) ... return re.sub('[. ]','',x) if newcol in colnames else newcol >>> for i in colnames: ... df = df.withColumnRenamed(i,colrename(i)) >>> df.printSchema() root |-- enghours: string (nullable = true) |-- eng_hours: string (nullable = true) |-- test_apt: string (nullable = true)
相关问答
更多-
pandas.DataFrame.replace上的文档说你必须提供一个嵌套字典 :第一个级别是你必须提供带有替换对的第二个字典 的列名 。 所以,这应该工作: >>> df=pd.DataFrame({'a': ['NÍCOLAS','asdč'], 'b': [3,4]}) >>> df a b 0 NÍCOLAS 3 1 asdč 4 >>> df.replace({'a': {'č': 'c', 'Í': 'I'}}, regex=True) a ...
-
您可以使用DataFrame.fillna()来替换空值。 替换na.fill()的空值,别名。 DataFrame.fillna()和DataFrameNaFunctions.fill()是彼此的别名。 参数: value - int,long,float,string或dict。 用来替换空值的值。 如果值是字典,则子集将被忽略,并且值必须是从列名(字符串)到替换值的映射。 替换值必须是int,long,float或string。 子集 - 要考虑的列名称的可选列表。 子集中指定的不具有匹配数据类型的列 ...
-
pyspark.sql.functions.substring(str,pos,len) 子串从pos开始,当str是String类型时长度为len,或者返回以字节为单位从pos开始的字节数组的片段,当str为二进制类型时,长度为len 在你的代码中, df.withColumn('COLUMN_NAME_fix', substring('COLUMN_NAME', 1, -1)) 1 is pos and -1 becomes len, length can't be -1 and so it retur ...
-
你可以编写一系列正则表达式,如: s = regexprep(s,'(?:À|Á|Â|Ã|Ä|Å)','A') s = regexprep(s,'(?:Ì|Í|Î|Ï)','I') 对于其他重音字符...(对于大写/小写) 警告:即使是拉丁字母的小部分,也有很多变体 一个更简单的例子: chars_old = 'ÁÉÍÓÚáéíóú'; chars_new = 'AEIOUaeiou'; str = 'Ámró'; [tf,loc] = ismember(str, chars_old); str(tf) ...
-
PySpark Dataframe:根据条件同时更改两列(PySpark Dataframe: Changing two Columns at the same time based on condition)[2022-02-26]
首先,您可以简单地将UDF添加为新列,将其用于计算并删除它: condition = is_special_id_udf(col("id"))) & should_hide_response_udf(col("response_created")) new_df = df.withColumn("tmp", condition).withColumn( "response_text", when(col("tmp"), None) .otherwise( ... -
pyspark数据帧列名(pyspark dataframe column name)[2024-03-25]
点用于结构类型内的嵌套字段。 因此,如果您有一个名为StructType类型的“地址”的列,并且您在其中有street1,street2等,您可以访问它的各个字段,如下所示: df.select("address.street1", "address.street2", ..) 因此,如果要在字段名称中使用点,则只要引用该字段就需要引用该字段。 例如: from pyspark.sql.types import * schema = StructType([StructField("my.field", ... -
我想你错过了 from pyspark.sql.types import FloatType 正如Pushkr建议udf with replace会给你返回字符串列,如果你不将结果转换为浮点数 from pyspark import SQLContext from pyspark.sql.functions import udf from pyspark.sql.types import FloatType from pyspark import SparkConf, SparkContext conf ...
-
使用lit可能更容易,如下所示: from pyspark.sql.functions import lit new_df = df.withColumn('column_name', lit(10)) It might be easier to use lit as follows: from pyspark.sql.functions import lit new_df = df.withColumn('column_name', lit(10))
-
循环遍历列,构造用null替换特定字符串的列表达式,然后select 列 : df.show() +----+----+----+---+ | v| x| y| z| +----+----+----+---+ | p| 1| 2|3.0| |NULL| 3|null|5.0| | NA|null| 6|7.0| | NaN| 8|null|NaN| +----+----+----+---+ import pyspark.sql.functions as F cols ...
-
做了一个小的改动,包括列名重复部分,请试试这个, >>> from pyspark.sql.types import * >>>import re >>> l=[('val1','val2','val3'),('val4','val5','val6')] >>> l_schema = StructType([StructField("eng hours",StringType(),True),StructField("eng_hours",StringType(),True),StructField("te ...