想象一下关系数据库中的2个表,例如Person和Billing.在这些实体之间定义了(非强制性)OneToOne关联,并且它们共享Person主键(即PERSON_ID在Person和Billing中定义,并且它是后者中的外键).
通过命名查询对Person进行选择时,例如:
from Person p where p.id = :id
Run Code Online (Sandbox Code Playgroud)
Hibernate/JPA生成两个选择查询,一个在Person表上,另一个在Billing表上.
上面的示例非常简单,并且不会导致任何性能问题,因为查询只返回一个结果.现在,假设Person有nOneToOne关系(均非强制性)与其他实体(所有共享Person主键).
如果我错了,请纠正我,但是select在Person上运行查询,返回r行,将导致(n+1)*rHibernate生成选择,即使这些关联是懒惰的.
是否存在针对此潜在性能灾难的解决方法(除了根本不使用共享主键)?谢谢你的所有想法.
我有两个名为 Banners 和 BannerGroup 的类。
\n\n我已经创建了双向关系。\n就我而言,一个 BannerGroup 可以有许多横幅,并且许多横幅属于同一个 BannerGroup。
\n\nMysql 表是...
\n\nmysql> desc Banners;\n+------------------+--------------+------+-----+---------+-----------------------------+\n| Field | Type | Null | Key | Default | Extra |\n+------------------+--------------+------+-----+---------+-----------------------------+\n| banner_id | int(11) | NO | PRI | NULL | auto_increment | \n| banner_name | varchar(255) | YES | | NULL | | \n| banner_group_id | int(11) | YES | MUL | NULL | | \n+------------------+--------------+------+-----+---------+-----------------------------+\n\nmysql> desc Banner_Group;\n+---------------------+--------------+------+-----+----------------+-----------------------------+\n| Field | Type | Null | Key | Default | Extra |\n+---------------------+--------------+------+-----+----------------+-----------------------------+\n| group_id …Run Code Online (Sandbox Code Playgroud) 我有一个@ManyToOne关系的实体,我想用一个查询检索,因此使用@Fetch(FetchMode.JOIN).有时Hibernate不尊重并发布N + 1 SELECT.随着有时我的意思是,因为我不知道是什么触发它,我有案件对不同的查询,这可能发生,或者不一样的类.
这是一个带有我使用的注释的简化实体:
@Entity
public class Employee {
@ManyToOne
@Fetch(FetchMode.JOIN)
private Department department;
}
Run Code Online (Sandbox Code Playgroud)
同
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
Root<Employee> root = criteriqQuery.from(Employee.class);
TypedQuery<Employee> typedQuery = entityManager.createQuery(criteriaQuery);
List<Employee> employees = typedQuery.getResultList();
Run Code Online (Sandbox Code Playgroud)
我希望一个查询来获取两个Employee及Department,像
select ... from Employee join Department on ...
Run Code Online (Sandbox Code Playgroud)
相反,我得到所有N的Employee第一个选择,然后SELECT是所有Departments的N s(考虑没有缓存).
我发现了许多类似的问题,但他们的答案提出了解决方法,并没有解释为什么会发生这种情况.请避免建议使用延迟加载的答案:这不是我问的问题.
我正在使用 spring boot、spring data 和 Hibernate 开发一个应用程序。我有一个实体“Client”,它与另一个实体“Reservation”具有“onetomany”关系,如下所示:
@Entity
public class Client implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany( mappedBy = "client", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.JOIN)
private Set<Reservation> reservations = new HashSet<>();
}
Run Code Online (Sandbox Code Playgroud)
我已经实现了 JpaRepository 接口,所以我可以操作客户端数据:
public interface ClientRepository extends JpaRepository<Client, Long> {}
Run Code Online (Sandbox Code Playgroud)
在服务类中,我实现了一种方法来查找数据库中的所有客户端:
@Service
public class ClientService {
@Autowired
private ClientRepository clientRepository;
@Transactional(readOnly = true)
public List<Client> findAll() {
return clientRepository.findAll();
}}
Run Code Online (Sandbox Code Playgroud)
findAll() 的执行运行良好并返回所有客户端及其保留。但是,在我的 sql 日志中,我已经执行了 N+1 个查询(N 是客户端的计数),尽管我已经fetch=FetchType.EAGER在我的客户端实体中设置了这些查询。我认为 hibernate 将创建一个单一的查询,在其中加入所有必要的数据以检索客户及其预订。
因此,我被迫通过查询明确加入客户和预订:
@Query("select client …Run Code Online (Sandbox Code Playgroud) 我有一个字符串ID为的实体:
@Table
@Entity
public class Stock {
@Id
@Column(nullable = false, length = 64)
private String index;
@Column(nullable = false)
private Integer price;
}
Run Code Online (Sandbox Code Playgroud)
和JpaRepository:
public interface StockRepository extends JpaRepository<Stock, String> {
}
Run Code Online (Sandbox Code Playgroud)
当我打电话时stockRepository::findAll,我有N + 1问题:
日志简化了
选择s.index,s.price from stock s
select s.index,s.price from stock s where s.index =?
报价的最后一行调用约5K次(表的大小).另外,当我更新价格时,我会做下一步:
stockRepository.save(listOfStocksWithUpdatedPrices);
Run Code Online (Sandbox Code Playgroud)
在日志中我有N插入.
当id是数字时,我没有看到类似的行为.
在我的情况下,PS将id的类型设置为数字不是最佳解决方案.
更新1:
我忘了提到还有Trade与多对多关系的类Stock:
@Table
@Entity
public class Trade {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column …Run Code Online (Sandbox Code Playgroud) 我知道hibernate是懒惰的默认提取策略,但有一些事情我不清楚,所以我希望你能解释一下.我想要做的是将标记为标记为起始图块的图块.
查询:
@NamedQuery(name = "Tile.findStartTileByGame",
query = "SELECT t FROM Tile t WHERE t.game = :game " +
"and t.startTile = true and t.blockWalkable = false")
Run Code Online (Sandbox Code Playgroud)
瓦:
public class Tile{
@OneToOne(mappedBy="tile")
private GameCharacter character;
@OneToOne(mappedBy="tile")
private GameObject gameObject;
Run Code Online (Sandbox Code Playgroud)
游戏:
@OneToMany(mappedBy="game")
private List<Tile> tiles;
Run Code Online (Sandbox Code Playgroud)
当我运行我的查询并且从不使用该对象时,hibernate仍然加入了我的角色和游戏对象.所以我有3个查询.我知道我可以通过fetch join来解决这个问题,但我的问题是为什么hibernate会同时获取两个引用?即使我使用fetch = FetchType.LAZY对它们进行注释,也会查询它.
我的DAO:
public static Tile getFreeStartTile(EntityManager em, Game game) {
TypedQuery<Tile> query = em.createNamedQuery("Tile.findStartTileByGame", Tile.class);
query.setParameter("game", game);
List<Tile> result = query.getResultList();
...
Run Code Online (Sandbox Code Playgroud)
在我解决这个问题之前,我想了解它为什么会发生.在此先感谢m