1+ /*
2+ * Hibernate, Relational Persistence for Idiomatic Java
3+ *
4+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+ * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+ */
7+ package org .hibernate .serialization ;
8+
9+ import java .io .Serializable ;
10+ import java .util .HashSet ;
11+ import java .util .Set ;
12+ import javax .persistence .Entity ;
13+ import javax .persistence .FetchType ;
14+ import javax .persistence .Id ;
15+ import javax .persistence .JoinColumn ;
16+ import javax .persistence .ManyToOne ;
17+ import javax .persistence .OneToMany ;
18+
19+ import org .hibernate .Hibernate ;
20+ import org .hibernate .Session ;
21+ import org .hibernate .Transaction ;
22+ import org .hibernate .annotations .Fetch ;
23+ import org .hibernate .annotations .FetchMode ;
24+ import org .hibernate .annotations .LazyCollection ;
25+ import org .hibernate .annotations .LazyCollectionOption ;
26+ import org .hibernate .annotations .LazyToOne ;
27+ import org .hibernate .annotations .LazyToOneOption ;
28+ import org .hibernate .cfg .AvailableSettings ;
29+ import org .hibernate .cfg .Configuration ;
30+ import org .hibernate .internal .util .SerializationHelper ;
31+ import org .hibernate .proxy .AbstractLazyInitializer ;
32+ import org .hibernate .proxy .HibernateProxy ;
33+
34+ import org .hibernate .testing .FailureExpected ;
35+ import org .hibernate .testing .TestForIssue ;
36+ import org .hibernate .testing .junit4 .BaseCoreFunctionalTestCase ;
37+ import org .junit .Before ;
38+ import org .junit .Test ;
39+
40+ import static org .junit .Assert .assertEquals ;
41+ import static org .junit .Assert .assertFalse ;
42+ import static org .junit .Assert .assertTrue ;
43+
44+ /**
45+ * @author Selaron
46+ */
47+ public class EntityProxySerializationTest extends BaseCoreFunctionalTestCase {
48+
49+ @ Override
50+ protected Class <?>[] getAnnotatedClasses () {
51+ return new Class [] { SimpleEntity .class , ChildEntity .class };
52+ }
53+
54+ @ Override
55+ protected void configure (final Configuration configuration ) {
56+ // enable LL without TX, which used to cause problems when serializing proxies (see HHH-12720)
57+ configuration .setProperty ( AvailableSettings .ENABLE_LAZY_LOAD_NO_TRANS , Boolean .TRUE .toString () );
58+ }
59+
60+ /**
61+ * Prepare and persist a {@link SimpleEntity} with two {@link ChildEntity}.
62+ */
63+ @ Before
64+ public void prepare () {
65+ final Session s = openSession ();
66+
67+ final Transaction t = s .beginTransaction ();
68+
69+ try {
70+ final Number count = (Number ) s .createQuery ("SELECT count(ID) FROM SimpleEntity" ).getSingleResult ();
71+ if (count .longValue () > 0L ) {
72+ // entity already added previously
73+ return ;
74+ }
75+
76+ final SimpleEntity entity = new SimpleEntity ();
77+ entity .setId ( 1L );
78+ entity .setName ( "TheParent" );
79+
80+ final ChildEntity c1 = new ChildEntity ();
81+ c1 .setId ( 1L );
82+ c1 .setParent ( entity );
83+
84+ final ChildEntity c2 = new ChildEntity ();
85+ c2 .setId ( 2L );
86+ c2 .setParent ( entity );
87+
88+ s .save ( entity );
89+ s .save ( c1 );
90+ s .save ( c2 );
91+ }
92+ finally {
93+ t .commit ();
94+ s .close ();
95+ }
96+ }
97+
98+ /**
99+ * Tests that lazy loading without transaction nor open session is generally
100+ * working. The magic is done by {@link AbstractLazyInitializer} who opens a
101+ * temporary session.
102+ */
103+ @ SuppressWarnings ("unchecked" )
104+ @ Test
105+ public void testProxyInitializationWithoutTX () {
106+ final Session s = openSession ();
107+
108+ final Transaction t = s .beginTransaction ();
109+ try {
110+ final ChildEntity child = s .find ( ChildEntity .class , 1L );
111+
112+ final SimpleEntity parent = child .getParent ();
113+
114+ t .rollback ();
115+ session .close ();
116+
117+ // assert we have an uninitialized proxy
118+ assertTrue ( parent instanceof HibernateProxy );
119+ assertFalse ( Hibernate .isInitialized ( parent ) );
120+
121+ assertEquals ( "TheParent" , parent .getName () );
122+
123+ // assert we have an initialized proxy now
124+ assertTrue ( Hibernate .isInitialized ( parent ) );
125+ }
126+ finally {
127+ if ( t .isActive () ) {
128+ t .rollback ();
129+ }
130+ s .close ();
131+ }
132+ }
133+
134+ /**
135+ * Tests that lazy loading without transaction nor open session is generally
136+ * working. The magic is done by {@link AbstractLazyInitializer} who opens a
137+ * temporary session.
138+ */
139+ @ SuppressWarnings ("unchecked" )
140+ @ Test
141+ @ TestForIssue (jiraKey = "HHH-12720" )
142+ @ FailureExpected (jiraKey = "HHH-12720" )
143+ public void testProxyInitializationWithoutTXAfterDeserialization () {
144+ final Session s = openSession ();
145+
146+ final Transaction t = s .beginTransaction ();
147+ try {
148+ final ChildEntity child = s .find ( ChildEntity .class , 1L );
149+
150+ final SimpleEntity parent = child .getParent ();
151+
152+ // destroy AbstractLazyInitializer internal state
153+ final SimpleEntity deserializedParent = (SimpleEntity ) SerializationHelper .clone ( parent );
154+
155+ t .rollback ();
156+ session .close ();
157+
158+ // assert we have an uninitialized proxy
159+ assertTrue ( deserializedParent instanceof HibernateProxy );
160+ assertFalse ( Hibernate .isInitialized ( deserializedParent ) );
161+
162+ assertEquals ( "TheParent" , deserializedParent .getName () );
163+
164+ // assert we have an initialized proxy now
165+ assertTrue ( Hibernate .isInitialized ( deserializedParent ) );
166+ }
167+ finally {
168+ if ( t .isActive () ) {
169+ t .rollback ();
170+ }
171+ s .close ();
172+ }
173+ }
174+
175+ @ Entity (name = "SimpleEntity" )
176+ static class SimpleEntity implements Serializable {
177+
178+ private Long id ;
179+
180+ private String name ;
181+
182+ Set <ChildEntity > children = new HashSet <>();
183+
184+ @ Id
185+ public Long getId () {
186+ return id ;
187+ }
188+
189+ public void setId (final Long id ) {
190+ this .id = id ;
191+ }
192+
193+ public String getName () {
194+ return name ;
195+ }
196+
197+ public void setName (final String name ) {
198+ this .name = name ;
199+ }
200+
201+ @ OneToMany (targetEntity = ChildEntity .class , mappedBy = "parent" )
202+ @ LazyCollection (LazyCollectionOption .EXTRA )
203+ @ Fetch (FetchMode .SELECT )
204+ public Set <ChildEntity > getChildren () {
205+ return children ;
206+ }
207+
208+ public void setChildren (final Set <ChildEntity > children ) {
209+ this .children = children ;
210+ }
211+
212+ }
213+
214+ @ Entity
215+ static class ChildEntity {
216+ private Long id ;
217+
218+ private SimpleEntity parent ;
219+
220+ @ Id
221+ public Long getId () {
222+ return id ;
223+ }
224+
225+ public void setId (final Long id ) {
226+ this .id = id ;
227+ }
228+
229+ @ ManyToOne (fetch = FetchType .LAZY )
230+ @ JoinColumn
231+ @ LazyToOne (LazyToOneOption .PROXY )
232+ public SimpleEntity getParent () {
233+ return parent ;
234+ }
235+
236+ public void setParent (final SimpleEntity parent ) {
237+ this .parent = parent ;
238+ }
239+
240+ }
241+ }
0 commit comments