티스토리 뷰

logback 을 DEBUG 로 설정했을때 stackOverflowError 가 발생했다.

 

Entity 의 toString 은 Lombok 의 @Data Annotation 을 이용해서 생성되어있다.

 

실제로 브레이크 포인트를 StringBuilder 와 String 의 valueOf 에 잡아보면 Request 를 처리하는 쓰레드에서 

toString 메서드가 재귀적으로 호출되며 종료조건없이 계속해서 호출되는것을 확인할 수 있다.

 

 

Entity.toString() 이 제귀적으로 호출되고 있다.

 

Entity 에는 3가지 @OneToMany 필드가 있고 모두 fetch = fetchType.Lazy 이다.

thread stack trace 를 보면 LazyFetch 인 Entity 필드의 toString 을 가져오기 위해서 PersistentBag 의 toString 이 호출된것을 확인할 수 있다.

 

 

persistentBag.toString()

public String toString() {
   read();
   return bag.toString();
}

 

Persistent 의 toString() 내부에서는 toString 을 호출하기전에 LazyFetch 인 Entity 들을 가져오기 위해 read() 를 호출한다. 

 

 

AbstractPersistentCollection.read()

protected final void read() {
   initialize( false );
}

 

read() 에서 LazyFetch 를 initialize 를 진행하면서 LogBack 설정이 Debug 일 경우에 Entity 의 toString 을 호출하는 로직이 존재한다.

 

 

Loader.java

/**
 * Called by subclasses that initialize collections
 */
public final void loadCollection(
        final SessionImplementor session,
        final Serializable id,
        final Type type) throws HibernateException {
 
   if ( LOG.isDebugEnabled() )
      LOG.debugf( "Loading collection: %s",
            MessageHelper.collectionInfoString( getCollectionPersisters()[0], id, getFactory() ) );
 
   Serializable[] ids = new Serializable[]{id};
   try {
      doQueryAndInitializeNonLazyCollections(
            session,
            new QueryParameters( new Type[]{type}, ids, ids ),
            true
         );
   }
   catch ( SQLException sqle ) {
      throw factory.getSQLExceptionHelper().convert(
            sqle,
            "could not initialize a collection: " +
            MessageHelper.collectionInfoString( getCollectionPersisters()[0], id, getFactory() ),
            getSQLString()
         );
   }
 
   LOG.debug( "Done loading collection" );
 
}

 

LOG.isDebugEnabled() 일 경우에 아래 명령이 수행된다.

LOG.debugf( "Loading collection: %s", MessageHelper.collectionInfoString( getCollectionPersisters()[0], id, getFactory() ) );

 

 

MessageHelper.collectionInfoString()

public static String collectionInfoString(
      CollectionPersister persister,
      Serializable id,
      SessionFactoryImplementor factory) {
   StringBuilder s = new StringBuilder();
   s.append( '[' );
   if ( persister == null ) {
      s.append( "<unreferenced>" );
   }
   else {
      s.append( persister.getRole() );
      s.append( '#' );
 
      if ( id == null ) {
         s.append( "<null>" );
      }
      else {
         addIdToCollectionInfoString( persister, id, factory, s );
      }
   }
   s.append( ']' );
 
   return s.toString();
}

 

 

위 메서드에서 persister 가 null 이 아니고 id 가 null 이 아니면  addIdToCollectionInfoString() 를 호출한다

 

MessageHelper.addIdToCollectionInfoString()

private static void addIdToCollectionInfoString(
      CollectionPersister persister,
      Serializable id,
      SessionFactoryImplementor factory,
      StringBuilder s ) {
   // Need to use the identifier type of the collection owner
   // since the incoming is value is actually the owner's id.
   // Using the collection's key type causes problems with
   // property-ref keys.
   // Also need to check that the expected identifier type matches
   // the given ID.  Due to property-ref keys, the collection key
   // may not be the owner key.
   Type ownerIdentifierType = persister.getOwnerEntityPersister()
         .getIdentifierType();
   if ( id.getClass().isAssignableFrom(
         ownerIdentifierType.getReturnedClass() ) ) {
      s.append( ownerIdentifierType.toLoggableString( id, factory ) );
   } else {
      // TODO: This is a crappy backup if a property-ref is used.
      // If the reference is an object w/o toString(), this isn't going to work.
      s.append( id.toString() );
   }
}

 

addIdToCollectionInfoString() 클래스 내에서 조건에 따라 id.toString() 을 호출하고 있다.

 

 

id 는 Entity 이기 때문에 Entity.toString() 이 다시 불리게 된다....

 

즉 Entity.toString() 이 다시 호출되게 된다.

 

Entity.toString() → LazyFetch Initialize → if (LOG.debugMode == true) → Entity.toString() 

무한 호출이 되게 된다...

 

LazyFetch Entity 에 대해서 toString 호출하는건 바람직 하지 않아 보인다..

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함