2013-02-18 14 views
19

Ho una piccola applicazione console e sto utilizzando spring-data-jpa con la sospensione. Non riesco davvero a capire come inizializzare le collezioni in modo pigro quando si utilizza spring-data-jpa con i suoi repository, in un'applicazione console standalone. Ecco alcuni del mio codice:come caricare la raccolta lenta quando si utilizza spring-data-jpa, con ibernazione, da un'applicazione console

@Entity 
public class User { 
... 
    @OneToMany(cascade=CascadeType.ALL) 
    @JoinColumn(name="USER_ORDER_ID") 
    private Set<Order> orders = new HashSet<Order>(); 
... 
} 

repository:

public interface UserRepository extends PagingAndSortingRepository<User, Long> { 

    public ArrayList<User> findByFirstNameIgnoreCase(String firstName); 
} 

impl di servizio:

@Service 
@Repository 
@Transactional 
public class UserServiceImpl implements UserService { 
    @Autowired 
    private UserRepository userRepository; 

public ArrayList<User> findByFirstNameIgnoreCase(String firstName) { 
    ArrayList<User> users = new ArrayList<User>(); 
    users = userRepository.findByFirstNameIgnoreCase(firstName); 
    return users; 
} 

mio metodo principale:

... 
user = userRepository.findByFirstNameIgnoreCase("john").get(0); 
orders = user.getOrders(); 
for (Order order : orders) { 
    LOGGER.info("getting orders: " + order.getId()); 
}  

il ciclo foreach ottiene un un'eccezione :

EVERE: Impossibile inizializzare pigramente una raccolta di ruolo: com.aki.util.User.orders, nessuna sessione o la sessione è stata chiusa org.hibernate.LazyInitializationException: Impossibile inizializzare pigramente una collezione di ruolo:

Si noti che non ho questo problema quando si esegue questo da una webapp con qualche tipo di OpenSessionInViewFilter.

+0

Controllare questo: http://stackoverflow.com/questions/15359306/how-to-load-lazy-fetched-items-from-hibernate-jpa-in- my-controller –

risposta

10

Una soluzione può essere quella di fare User.orders un entusiasmo esagerato collezione da

@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER) 
private Set<Order> orders = new HashSet<Order>(); 

associazioni di entità sono pigramente caricato di default. Ciò significa che il set orders è in realtà solo un oggetto proxy che non verrà inizializzato finché non si invoca un metodo su di esso. Questo è buono, perché gli oggetti Order associati non verranno caricati a meno che non siano necessari. Tuttavia, questo può causare problemi se si tenta di accedere alla raccolta non inizializzata al di fuori di una transazione in esecuzione.

Se si sa che nella maggior parte dei casi è necessario l'Ordine dell'utente, è logico che l'associazione venga recuperata con entusiasmo. Altrimenti dovrai assicurarti che la raccolta venga inizializzata/caricata all'interno di una transazione. Lo OpenSessionInViewFilter che hai citato si assicura che la transazione rimanga aperta durante l'elaborazione della richiesta, ecco perché non hai questo problema nella tua app web.

Nel caso in cui si deve tenere pigramente caricato, provare a utilizzare Primavera di TransactionTemplate per avvolgere il codice nel metodo principale:

TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); 
transactionTemplate.execute(new TransactionCallbackWithoutResult() { 
    @Override 
    protected void doInTransactionWithoutResult(TransactionStatus status) { 
    ... 
    } 
}); 
+2

questo non è quello che sto cercando. forse non ero chiaro che volevo un'inizializzazione pigra.Ho modificato la mia domanda. – aki

+0

In tal caso, vedere la mia risposta modificata per l'uso di un 'TransactionTemplate'. – zagyi

+0

Odio davvero lavorare con TransactionTemplate, ma funziona. Nessun altro suggerimento? – aki

7

Tuttavia, ho trovato un modo. Questo metodo è dentro la mia implementazione del servizio:

public Set<Order> fetchUserOrders(Long userId) { 
    User user = userRepository.findOne(userId); 
    Hibernate.initialize(user.getOrders()); 
    Set<Order> orders = user.getOrders(); 
    return orders; 
} 

Nota: questo è come @zagyi sugested in uno dei suoi commenti, ma anche prestare attenzione alla dichiarazione: Hibernate.initialize(user.getOrders()); senza questo la raccolta ancora non sarebbe inizializzato e si otterrà un errore.

+6

Solo un commento: 'Hibernate.initialize()' legherà la tua applicazione a Hibernate. Va bene se non ti interessa. Nel caso ti interessi, usa 'user.getOrders(). Size()'. Anche se questo non riflette affatto la tua intenzione, non si basa su chiamate non JPA, e puoi sempre dichiarare un metodo di supporto con un nome più descrittivo che riceve una raccolta e chiama semplicemente 'size()' su di esso. – zagyi

+0

Questo funziona. Hai ragione, è molto meglio usare user.getOrders(). Size. Non che io vada a passare, dall'ibernazione, in qualunque momento presto. Potrei giurare che l'ho provato prima. Anche se non sono sicuro che fosse dal livello di servizio :). Comunque grazie mille per il tuo aiuto. – aki

+0

Ciò significa che questo va nel ciclo for per ottenere tutti gli utenti con gli ordini? Diciamo che sto tirando findAll su Users, ovvero userWithOrders, devo fare Hibernate.initialize su ogni utente eseguendo il loop? So che EAGER avrebbe funzionato, ma in caso di pigro, è l'unico modo? – PavanSandeep

-2

questo ha funzionato per me:

@Component 
public class Test { 

    @Inject 
    OrderDAO orderDAO; 

    @PersistenceUnit 
    private EntityManagerFactory emf; 

    @PostConstruct 
    public void test(){ 
     EntityManager em= emf.createEntityManager(); 
     for (Order o:orderDAO.findAll()){ 
      o=em.find(o.getClass(),o.getId()); 
      System.out.println(o.getLazyField()); 
     } 
     em.close(); 
    } 
} 
Problemi correlati