Java Hibernate不会加载一对多关系集,即使使用了“急切获取”
我有下面的hibernate映射Java Hibernate不会加载一对多关系集,即使使用了“急切获取”,java,spring,hibernate,orm,hibernate-mapping,Java,Spring,Hibernate,Orm,Hibernate Mapping,我有下面的hibernate映射 @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "product") private Set<ProductLicense> productLicenses = new HashSet<ProductLicense>(0); @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "product")
private Set<ProductLicense> productLicenses = new HashSet<ProductLicense>(0);
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "product_id", nullable = false)
private Product product;
下面是ProductDaoImpl类
@Repository
public class ProductDaoImpl implments ProductDao{
@Autowired
private SessionFactory sessionFactory;
@Override
public Product getProduct(Integer productId)
{
Product result = (Product) sessionFactory.getCurrentSession().get(Product.class, productId);
Hibernate.initialize(result.getProductLicenses());
//sessionFactory.getCurrentSession().refresh(result);
return result;
}
.. other methods
}
另外,如果我调用Hibernate.initialize(产品);不在表之间执行任何联接。
指令sessionFactory.getCurrentSession().get(Product.class,productId);不生成对数据库的任何查询(我有属性show_sql equals to true)。
但如果我取消对sessionFactory.getCurrentSession()的注释,请刷新(结果);我可以看到sql和集合已加载,我无法理解为什么在另一种情况下未加载。
但我无法理解映射中的错误
产品类别为:
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "product_id", unique = true)
private Integer productId;
@NotNull
@Size(max = 200)
@Column(name = "name")
private String name;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "product")
private Set<ProductLicense> productLicenses = new HashSet<ProductLicense>(0);
public Set<ProductLicense> getProductLicenses()
{
return productLicenses;
}
public void setProductLicenses(Set<ProductLicense> productLicenses)
{
this.productLicenses = productLicenses;
}
//getters and setters
..
}
最后,我的配置如下:
@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.company.package")
@PropertySource("classpath:application.properties")
public class WebAppConfig {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
@Resource
private Environment env;
@Bean
public DataSource dataSource()
{
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return dataSource;
}
private Properties hibProperties()
{
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT,
env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL,
env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
properties.put("hibernate.cache.provider_class", "org.hibernate.cache.NoCacheProvider");
properties.put("hibernate.cache.use_second_level_cache", "false");
return properties;
}
@Bean
public LocalSessionFactoryBean sessionFactory()
{
LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
lsfb.setDataSource(this.dataSource());
lsfb.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
lsfb.setHibernateProperties(this.hibProperties());
return lsfb;
}
@Bean
public HibernateTransactionManager transactionManager()
{
return new HibernateTransactionManager(this.sessionFactory().getObject());
}
}
Product product = (Product) session.get(Product.class, productId);
ProductLicense license = new ProductLicense();
license.setProduct(product);
session.persist(license);
我的测试:
@Before
@Transactional
public void setUp() throws ParseException
{
product1 = new Product();
..
productService.addProduct(product1);
product2 = new Product();
..
product2 = productService.addProduct(product2);
productService.addProductLicense(product2.getProductId(), licenceKey1Product2);
productService.addProductLicense(product2.getProductId(), licenceKey2Product2);
}
@Test
@Transactional
public void addProductLicenseTest()
{
String licenseKey[] = { "thisisthekey", "thisisthekey2" };
productService.addProductLicense(product1.getProductId(), licenseKey[0]);
//sessionFactory.getCurrentSession().flush();
//sessionFactory.getCurrentSession().clear();
Product product1saved = productService.getProduct(product1.getProductId());
assertEquals(1, product1saved.getProductLicenses().size());
assertEquals(licenseKey[0], product1saved.getProductLicenses().iterator().next().getKey());
productService.addProductLicense(product1.getProductId(), licenseKey[1]);
product1saved = productService.getProduct(product1saved.getProductId());
assertEquals(2, product1saved.getProductLicenses().size());
}
下面是(最有可能)发生的情况
您的整个测试方法都是事务性的。因此,服务调用与测试本身的代码在同一事务中发生
在测试中,您正在创建和持久化一个产品,而没有任何许可证。这会将产品实例存储在会话缓存中,并与事务绑定。此产品的许可证列表为空
然后调用一个服务方法(仍在同一事务中),该方法创建一个许可证,其产品就是您之前创建的产品。我猜这项服务的代码如下所示:
@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.company.package")
@PropertySource("classpath:application.properties")
public class WebAppConfig {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
@Resource
private Environment env;
@Bean
public DataSource dataSource()
{
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return dataSource;
}
private Properties hibProperties()
{
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT,
env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL,
env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
properties.put("hibernate.cache.provider_class", "org.hibernate.cache.NoCacheProvider");
properties.put("hibernate.cache.use_second_level_cache", "false");
return properties;
}
@Bean
public LocalSessionFactoryBean sessionFactory()
{
LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
lsfb.setDataSource(this.dataSource());
lsfb.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
lsfb.setHibernateProperties(this.hibProperties());
return lsfb;
}
@Bean
public HibernateTransactionManager transactionManager()
{
return new HibernateTransactionManager(this.sessionFactory().getObject());
}
}
Product product = (Product) session.get(Product.class, productId);
ProductLicense license = new ProductLicense();
license.setProduct(product);
session.persist(license);
在上面的代码中,从会话缓存中获取产品,然后使用找到的产品保留许可证。请注意,代码设置了许可证的产品,但没有将许可证添加到产品中,这就是问题所在
然后您将通过ID获得产品,仍然在同一事务中。因此,Hibernate直接从缓存中检索产品。在缓存中,由于您忽略了向产品添加许可证,因此许可证列表仍然为空
因此,简而言之,维护双向关联的双方是您的责任。如果只初始化所有者端,Hibernate将保持关联,并且如果从数据库加载产品,则将正确初始化列表。但是,当产品在缓存中时,它会保持在您将其存储在缓存中的状态。这就是为什么您会看到一个空列表,除非您明确地清除会话,从而从数据库重新加载状态。如何测试它?在Junit上,在标有“@test”和“@Transactional”的测试中,我调用product1saved=productService.getProduct(product1.getProductId());通过编辑您的question@JBNizet我注意到添加sessionFactory.getCurrentSession().flush();sessionFactory.getCurrentSession().clear();在productService.getProduct(product1.getProductId())之前;在单元测试中对我帮助很大,但现在我想知道为什么hibernate在junit中会有这种奇怪的行为?我可能能够解释为什么您的代码的行为与您看到的一样,但不是没有看到代码。张贴测试代码。是的,我同意你的看法。但这是在Hibernate缓存的第一级中发生的事情。因此,我想我只有在单元测试中才会有这种奇怪的行为,因为它们是在同一个事务下执行的。如果您的生产代码还添加了许可证,并且希望该许可证在产品列表中,那么您的生产代码中也会有同样的问题。这取决于你。为愚蠢的观察抱歉,但如果在我的代码会话后。更新(产品);我添加session.flush();这对我有帮助吗?我怀疑hibernate无法为我更新双方的一对多关系。因此,这意味着在上面编写的代码之后,我必须添加一个product.getlicenses().add(license);更新(产品);这有点奇怪,我不需要只执行session.update(许可证)?不,同花顺没用。flush只执行暂停插入/删除/更新查询。Hibernat不会为您同时更新双方。这是你的责任。我也有同样的问题,刷新没有帮助,如何轻松地重新加载到JPA?我用单向和渴望