NHibernate 试图加载一个小的数据层次结构时遇到了问题。我的域模型如下所示:
class GrandParent
{
int ID{get;set;}
IList<Parent> Parents {get; set;}
}
class Parent
{
IList<Child> Children {get; set;}
}
class Child
{
}
Run Code Online (Sandbox Code Playgroud)
我想为给定的祖父母加载所有父母和孩子。这个 Linq-to-NH 查询创建了正确的 SQL 并按预期加载了 GrandParent:(该示例假设祖父母有 2 个父母,每个父母都有 2 个子对象 - 所以总共有 4 个子对象)。
var linq = session.Linq<GrandParent>();
linq.Expand("Parents");
linq.Expand("Parents.Children");
linq.QueryOptions.RegisterCustomAction(c =>
c.SetResultTransformer(new DistinctRootEntityResultTransformer()));
var grandparent = (select g from session.Linq<GrandParent>()
where g.ID == 1
select g).ToList();
Assert(grandparent.Count == 1); //Works
Assert(grandparent.Parents.Count == 2); //Fails - count = 4!
Run Code Online (Sandbox Code Playgroud)
Grandparent.Parents 集合包含 4 个项目,其中 2 个是重复的。似乎 DistinctRootEntityResultTransformer …
我有以下模型:Game 和 Pick。Game 和 Pick 之间存在一对多的关联。还有第三个模型叫做 Player,一个 Player 有很多 Picks。
Player 类中有一个方法可以为给定的游戏找到一个选择,或者如果它不存在则创建一个新的。
class Player < ActiveRecord::Base
has_many :picks
def pick_for_game(game)
game_id = game.instance_of?(Game) ? game.id : game
picks.find_or_initialize_by_game_id(game_id)
end
end
Run Code Online (Sandbox Code Playgroud)
我想急切地为每个选择加载游戏。但是,如果我这样做
picks.find_or_initialize_by_game_id(game_id, :include => :game)
Run Code Online (Sandbox Code Playgroud)
它首先在此查询运行时获取选择(该方法运行多次),然后在访问每个选择时获取游戏。如果我将 default_scope 添加到 Pick 类
class Pick < ActiveRecord::Base
belongs_to :game
belongs_to :player
default_scope :include => :game
end
Run Code Online (Sandbox Code Playgroud)
它仍然为每个选择生成 2 个选择语句,但现在它在选择后立即加载游戏,但它仍然没有像我期望的那样进行连接。
Pick Load (0.2ms) SELECT "picks".* FROM "picks" WHERE "picks"."game_id" = 1 AND ("picks".player_id = 1) LIMIT 1
Game Load (0.4ms) SELECT "games".* FROM "games" …Run Code Online (Sandbox Code Playgroud) 我有一个简单的查询,我想这样做:
1)Products有ChildProducts哪些有PriceTiers
2)我想得到所有Productsa Category的a ID为1和Display= true.
3)我想包含所有ChildProducts有Display= true的东西.
4)然后包括PriceTiershas IsActive= true.
根据我的阅读,EF不支持使用过滤器进行预先加载,因此以下内容不起作用:
ProductRepository.Query.IncludeCollection(Function(x) x.ChildProducts.Where(Function(y) y.Display).Select(Function(z) z.PriceTiers.Where(Function(q) q.IsActive))).Where(Function(x) x.Categories.Any(Function(y) y.ID = ID)))
Run Code Online (Sandbox Code Playgroud)
有什么建议?
我有一个自我加入的模型:
class Comment < ActiveRecord::Base
belongs_to :parent, :class_name => 'Comment', :foreign_key => 'parent_id'
has_many :children, :class_name => 'Comment', :foreign_key => "parent_id"
end
Run Code Online (Sandbox Code Playgroud)
后来我希望首先进行1次SQL调用以获取所有相关注释,然后递归渲染它们.
我如何递归加载?
comments = Comment.where(:post_id => @post_id).includes(:comments=> [:comments => [:comments .... #How do I get this to recursively eager load?
def show_comments(comment)
render comment
comment.children.each do |child|
show_comments(child)
end
end
Run Code Online (Sandbox Code Playgroud) 我将 Symfony2 与 Doctrine2(最新版本)一起使用,并定义了这种关系:
/**
* @ORM\OneToMany(targetEntity="Field", mappedBy="event", fetch="EAGER")
* @ORM\OrderBy({"name" = "ASC"})
*/
protected $fields;
Run Code Online (Sandbox Code Playgroud)
关系的另一边定义为:
/**
* @ORM\ManyToOne(targetEntity="Event", inversedBy="fields", fetch="EAGER")
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
protected $event;
Run Code Online (Sandbox Code Playgroud)
在执行“fetchOnyById”时,Doctrine 运行 2 个查询。1 获取对象本身,1 获取相关字段。我希望这是一个连接,但它不是。
在控制器中完成后,我将对象传递给树枝。在那里我再次检索字段作为对象的属性。这会导致运行另一个查询以再次检索字段。
Clearly I'm doing something wrong, as I would expect only 1 query to be run and 3 are actually run.
我有一个属于类别的条目模型。类别模型使用祖先,以便我可以嵌套类别。
我想按其根类别对所选条目进行分组。我遇到的问题是,为了做到这一点,rails 对每个条目执行查询以获取根类别。因此,如果我有 20 条记录,它将执行 20 个查询以按根类别分组。
我已经通过预先加载类别(如下所示)减少了查询数量,但我不知道如何预先加载类别的根。
这是我正在使用的代码:
控制器
@entries = current_user.entries.includes(:category)
@entries_by_category = @entries.group_by { |entry| entry.category.root }
Run Code Online (Sandbox Code Playgroud)
入口.rb
class Entry < ActiveRecord::Base
belongs_to :category
end
Run Code Online (Sandbox Code Playgroud)
类别.rb
class Category < ActiveRecord::Base
has_ancestry
has_many :entries
end
Run Code Online (Sandbox Code Playgroud)
我试图把默认范围的分类模型,像这样:default_scope { includes(:ancestry) }。但这并没有改变执行查询的数量。而且我无法弄清楚如何includes()在原始查询上使用以包含类别和类别的根。
作为一些额外的信息,类别只能嵌套 2 层深(仅类别和子类别)。所以 root 在技术上意味着父级,它不必遍历多个级别。
有任何想法吗?
考虑以下实体模型:
public class Parent
{
public virtual FirstChild FirstChild { get; set; }
public virtual SecondChild SecondChild { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
在我的代码中,我加载了Parent实体:
Parent parent = <loaded in some way>;
Run Code Online (Sandbox Code Playgroud)
要显式加载其导航属性,我使用
db.Entry(parent).Reference(p => p.FirstChild).Load();
db.Entry(parent).Reference(p => p.SecondChild).Load();
Run Code Online (Sandbox Code Playgroud)
但这会导致两个数据库查询。
问题:有没有更优雅的方式,允许在单个查询中显式加载多个导航属性?
如果我没有parent加载,我会立即加载:
Parent parent = db.Parents
.Include(p => p.FirstChild)
.Include(p => p.SecondChild)
.FirstOrDefault();
Run Code Online (Sandbox Code Playgroud)
但是,正如我所提到的,我已经在没有相关实体的情况下加载了它(并且我无法修改加载代码)。
我有一张有很多预订的餐馆的桌子。现在我想查询所有餐馆,如果他们有 2 人的预订,它应该预加载这些关联。如果没有 2 的预订,它仍应返回餐厅,但关联为空。
为此,我尝试了以下查询:
Restaurant.eager_load(:reservations).where("number_of_people = ?", 2)
Run Code Online (Sandbox Code Playgroud)
但是,这不起作用,因为它会丢弃所有有预订但都不是 2 人的餐厅。
所以我想做的是将该条件移动到连接条件中。就像是:
Restaurant.joins('LEFT OUTER JOIN \"reservations\" ON \"reservations\".\"restaurant_id\" = \"restaurants\".\"id\" AND \"reservations\".\"number_of_people\" = ?', 2)
Run Code Online (Sandbox Code Playgroud)
这将给出我需要的结果,但是这不是预加载“保留”关联而是导致 N+1 问题。
似乎eager_load 不接受自定义查询。我找到了这些线程:https : //github.com/rails/rails/issues/12270和JOIN ON ... AND 在 ActiveRecord 中急切加载关联时的条件,但没有提供解决方案。
我目前发现您可以对原始 sql 查询进行水合。
我有以下查询:
DB::table(DB::raw('(SELECT *, Y(location) AS longitude, X(location) AS latitude FROM meetings WHERE MBRCONTAINS(@quadrat, location)) AS sub'))
->select(DB::raw('(FLOOR(SQRT(POW((@ibk_breite - sub.latitude) * 111, 2) + POW((@ibk_laenge - sub.longitude) * 111 * ABS(COS(RADIANS(@ibk_breite))),2)))) AS distance, sub.*, latitude, longitude'));
Run Code Online (Sandbox Code Playgroud)
我按以下方式补水
$meetings = Meeting::fromQuery($query->toSql());
Run Code Online (Sandbox Code Playgroud)
在刀片视图中,我需要从不同的表中获取一些额外的数据,例如:
$meeting->user
Run Code Online (Sandbox Code Playgroud)
其中引用了用户模型。但是,如果我不是完全错误的,那会在 for each 循环中导致 n+1 问题,因为我并不急于加载它?!那么是否可以像通常那样急切加载所需的模型
->with('user', 'books', 'etc...')
Run Code Online (Sandbox Code Playgroud)
??
也可以像$meetings = $query->paginate(5);这样对它进行分页 并做$meetings->withPath('home');
编辑:找到了一个解决方案:
// Do your query stuff
// Get count before the query because it won't work with skip and …Run Code Online (Sandbox Code Playgroud) 我正在创建一个Reply模型,然后尝试返回具有所有者关系的对象。这是返回空对象的代码:
//file: Thread.php
//this returns an empty object !!??
public function addReply($reply)
{
$new_reply = $this->replies()->create($reply);
return $new_reply->with('owner');
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我将with()方法换成load()方法以加载所有者关系,则可以得到预期的结果。那就是返回的回复对象及其相关的所有者关系:
//this works
{
$new_reply = $this->replies()->create($reply);
return $new_reply->load('owner');
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么。寻找澄清。
谢谢,耶
eager-loading ×10
activerecord ×4
c# ×2
laravel-5.4 ×2
ancestry ×1
doctrine-orm ×1
eloquent ×1
group-by ×1
laravel-5 ×1
nhibernate ×1
php ×1
symfony ×1