Skip to main content

sz_orm_core/
lazy_loader.rs

1#![allow(missing_docs)]
2//! 懒加载器(Lazy Loader)
3//!
4//! 对标 Hibernate `@LazyCollection` / EF Core `Virtual` 导航属性。
5//!
6//! 在首次访问关联实体时才从数据库加载,避免不必要的查询和内存占用。
7//!
8//! # 使用示例
9//!
10//! ```
11//! use sz_orm_core::lazy_loader::LazyRef;
12//!
13//! // 创建懒加载引用(传入加载闭包)
14//! let lazy_user: LazyRef<String> = LazyRef::new(|| "loaded user".to_string());
15//!
16//! // 首次访问触发加载
17//! assert!(!lazy_user.is_loaded());
18//! let user = lazy_user.get().unwrap();
19//! assert_eq!(user, "loaded user");
20//! assert!(lazy_user.is_loaded()); // 已缓存
21//! ```
22
23use std::sync::{Arc, Mutex};
24
25/// 懒加载引用
26///
27/// 包装一个值和其加载闭包,首次 `get()` 时触发加载并缓存结果。
28pub struct LazyRef<T> {
29    value: Arc<Mutex<Option<T>>>,
30    loader: Arc<dyn Fn() -> T + Send + Sync>,
31}
32
33impl<T: Clone + Send + Sync + 'static> LazyRef<T> {
34    /// 创建懒加载引用
35    pub fn new<F>(loader: F) -> Self
36    where
37        F: Fn() -> T + Send + Sync + 'static,
38    {
39        Self {
40            value: Arc::new(Mutex::new(None)),
41            loader: Arc::new(loader),
42        }
43    }
44
45    /// 创建已加载的引用(用于测试或预加载数据)
46    pub fn loaded(value: T) -> Self {
47        Self {
48            value: Arc::new(Mutex::new(Some(value))),
49            loader: Arc::new(|| panic!("LazyRef::loaded should not call loader")),
50        }
51    }
52
53    /// 获取值(首次调用触发加载)
54    pub fn get(&self) -> Option<T> {
55        let mut guard = self.value.lock().unwrap();
56        if guard.is_none() {
57            *guard = Some((self.loader)());
58        }
59        guard.clone()
60    }
61
62    /// 是否已加载
63    pub fn is_loaded(&self) -> bool {
64        self.value.lock().unwrap().is_some()
65    }
66
67    /// 强制重新加载
68    pub fn reload(&self) -> Option<T> {
69        let mut guard = self.value.lock().unwrap();
70        *guard = Some((self.loader)());
71        guard.clone()
72    }
73
74    /// 清除缓存(下次 get 会重新加载)
75    pub fn invalidate(&self) {
76        *self.value.lock().unwrap() = None;
77    }
78}
79
80impl<T: Clone + Send + Sync + 'static> Clone for LazyRef<T> {
81    fn clone(&self) -> Self {
82        Self {
83            value: Arc::clone(&self.value),
84            loader: Arc::clone(&self.loader),
85        }
86    }
87}
88
89/// 懒加载集合
90///
91/// 用于懒加载一对多关系(如 User → Orders)。
92pub struct LazyCollection<T> {
93    inner: LazyRef<Vec<T>>,
94}
95
96impl<T: Clone + Send + Sync + 'static> LazyCollection<T> {
97    pub fn new<F>(loader: F) -> Self
98    where
99        F: Fn() -> Vec<T> + Send + Sync + 'static,
100    {
101        Self {
102            inner: LazyRef::new(loader),
103        }
104    }
105
106    pub fn loaded(items: Vec<T>) -> Self {
107        Self {
108            inner: LazyRef::loaded(items),
109        }
110    }
111
112    /// 获取所有元素
113    pub fn all(&self) -> Vec<T> {
114        self.inner.get().unwrap_or_default()
115    }
116
117    /// 获取数量
118    pub fn len(&self) -> usize {
119        self.all().len()
120    }
121
122    /// 是否为空
123    pub fn is_empty(&self) -> bool {
124        self.len() == 0
125    }
126
127    /// 是否已加载
128    pub fn is_loaded(&self) -> bool {
129        self.inner.is_loaded()
130    }
131
132    /// 过滤已加载的元素
133    pub fn filter<F>(&self, predicate: F) -> Vec<T>
134    where
135        F: Fn(&T) -> bool,
136    {
137        self.all().into_iter().filter(predicate).collect()
138    }
139
140    /// 清除缓存
141    pub fn invalidate(&self) {
142        self.inner.invalidate();
143    }
144}
145
146impl<T: Clone + Send + Sync + 'static> Clone for LazyCollection<T> {
147    fn clone(&self) -> Self {
148        Self {
149            inner: self.inner.clone(),
150        }
151    }
152}
153
154/// 懒加载器
155///
156/// 管理多个懒加载引用,提供统一的加载和缓存管理。
157pub struct LazyLoader {
158    load_count: Arc<Mutex<usize>>,
159}
160
161impl Default for LazyLoader {
162    fn default() -> Self {
163        Self::new()
164    }
165}
166
167impl LazyLoader {
168    pub fn new() -> Self {
169        Self {
170            load_count: Arc::new(Mutex::new(0)),
171        }
172    }
173
174    /// 创建懒加载引用,并统计加载次数
175    pub fn lazy<F, T>(&self, loader: F) -> LazyRef<T>
176    where
177        T: Clone + Send + Sync + 'static,
178        F: Fn() -> T + Send + Sync + 'static,
179    {
180        let count = Arc::clone(&self.load_count);
181        LazyRef::new(move || {
182            *count.lock().unwrap() += 1;
183            loader()
184        })
185    }
186
187    /// 获取总加载次数
188    pub fn load_count(&self) -> usize {
189        *self.load_count.lock().unwrap()
190    }
191
192    /// 重置加载计数
193    pub fn reset_count(&self) {
194        *self.load_count.lock().unwrap() = 0;
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn test_lazy_ref_first_access_loads() {
204        let lazy: LazyRef<String> = LazyRef::new(|| "hello".to_string());
205        assert!(!lazy.is_loaded());
206        let val = lazy.get().unwrap();
207        assert_eq!(val, "hello");
208        assert!(lazy.is_loaded());
209    }
210
211    #[test]
212    fn test_lazy_ref_cached_after_load() {
213        let counter = Arc::new(Mutex::new(0));
214        let c = Arc::clone(&counter);
215        let lazy: LazyRef<i32> = LazyRef::new(move || {
216            *c.lock().unwrap() += 1;
217            42
218        });
219
220        assert_eq!(lazy.get().unwrap(), 42);
221        assert_eq!(lazy.get().unwrap(), 42);
222        assert_eq!(lazy.get().unwrap(), 42);
223        assert_eq!(*counter.lock().unwrap(), 1);
224    }
225
226    #[test]
227    fn test_lazy_ref_reload() {
228        let lazy: LazyRef<i32> = LazyRef::new(|| 1);
229        assert_eq!(lazy.get().unwrap(), 1);
230        assert_eq!(lazy.get().unwrap(), 1);
231    }
232
233    #[test]
234    fn test_lazy_ref_invalidate() {
235        let counter = Arc::new(Mutex::new(0));
236        let c = Arc::clone(&counter);
237        let lazy: LazyRef<i32> = LazyRef::new(move || {
238            let mut g = c.lock().unwrap();
239            *g += 1;
240            *g
241        });
242
243        assert_eq!(lazy.get().unwrap(), 1);
244        lazy.invalidate();
245        assert!(!lazy.is_loaded());
246        assert_eq!(lazy.get().unwrap(), 2);
247    }
248
249    #[test]
250    fn test_lazy_ref_loaded() {
251        let lazy: LazyRef<String> = LazyRef::loaded("preloaded".to_string());
252        assert!(lazy.is_loaded());
253        assert_eq!(lazy.get().unwrap(), "preloaded");
254    }
255
256    #[test]
257    fn test_lazy_ref_clone_shares_state() {
258        let lazy: LazyRef<i32> = LazyRef::new(|| 99);
259        let lazy2 = lazy.clone();
260        lazy.get();
261        assert!(lazy2.is_loaded());
262    }
263
264    #[test]
265    fn test_lazy_collection_basic() {
266        let coll: LazyCollection<i32> = LazyCollection::new(|| vec![1, 2, 3]);
267        assert!(!coll.is_loaded());
268        assert_eq!(coll.len(), 3);
269        assert!(!coll.is_empty());
270        assert!(coll.is_loaded());
271    }
272
273    #[test]
274    fn test_lazy_collection_filter() {
275        let coll: LazyCollection<i32> = LazyCollection::new(|| vec![1, 2, 3, 4, 5]);
276        let evens = coll.filter(|x| x % 2 == 0);
277        assert_eq!(evens, vec![2, 4]);
278    }
279
280    #[test]
281    fn test_lazy_collection_empty() {
282        let coll: LazyCollection<i32> = LazyCollection::new(std::vec::Vec::new);
283        assert_eq!(coll.len(), 0);
284        assert!(coll.is_empty());
285    }
286
287    #[test]
288    fn test_lazy_collection_loaded() {
289        let coll: LazyCollection<i32> = LazyCollection::loaded(vec![10, 20]);
290        assert!(coll.is_loaded());
291        assert_eq!(coll.all(), vec![10, 20]);
292    }
293
294    #[test]
295    fn test_lazy_loader_count() {
296        let loader = LazyLoader::new();
297        let lazy1: LazyRef<i32> = loader.lazy(|| 1);
298        let lazy2: LazyRef<i32> = loader.lazy(|| 2);
299
300        assert_eq!(loader.load_count(), 0);
301        lazy1.get();
302        assert_eq!(loader.load_count(), 1);
303        lazy2.get();
304        assert_eq!(loader.load_count(), 2);
305        lazy1.get();
306        assert_eq!(loader.load_count(), 2);
307    }
308
309    #[test]
310    fn test_lazy_loader_reset() {
311        let loader = LazyLoader::new();
312        let lazy: LazyRef<i32> = loader.lazy(|| 42);
313        lazy.get();
314        assert_eq!(loader.load_count(), 1);
315        loader.reset_count();
316        assert_eq!(loader.load_count(), 0);
317    }
318
319    #[test]
320    fn test_e2e_user_orders_lazy_loading() {
321        #[derive(Clone)]
322        #[allow(dead_code)]
323        struct Order {
324            id: i64,
325            user_id: i64,
326            amount: f64,
327        }
328
329        #[derive(Clone)]
330        #[allow(dead_code)]
331        struct User {
332            id: i64,
333            name: String,
334            orders: LazyCollection<Order>,
335        }
336
337        let query_count = Arc::new(Mutex::new(0));
338        let qc = Arc::clone(&query_count);
339
340        let user = User {
341            id: 1,
342            name: "alice".into(),
343            orders: LazyCollection::new(move || {
344                *qc.lock().unwrap() += 1;
345                vec![
346                    Order {
347                        id: 101,
348                        user_id: 1,
349                        amount: 99.5,
350                    },
351                    Order {
352                        id: 102,
353                        user_id: 1,
354                        amount: 200.0,
355                    },
356                ]
357            }),
358        };
359
360        assert!(!user.orders.is_loaded());
361        assert_eq!(*query_count.lock().unwrap(), 0);
362
363        let all_orders = user.orders.all();
364        assert_eq!(all_orders.len(), 2);
365        assert_eq!(all_orders[0].id, 101);
366        assert_eq!(all_orders[1].amount, 200.0);
367        assert!(user.orders.is_loaded());
368        assert_eq!(*query_count.lock().unwrap(), 1);
369
370        let _again = user.orders.all();
371        assert_eq!(*query_count.lock().unwrap(), 1);
372
373        let big_orders = user.orders.filter(|o| o.amount >= 200.0);
374        assert_eq!(big_orders.len(), 1);
375        assert_eq!(big_orders[0].id, 102);
376        assert_eq!(*query_count.lock().unwrap(), 1);
377    }
378
379    #[test]
380    fn test_e2e_lazy_ref_belongs_to() {
381        #[derive(Clone)]
382        #[allow(dead_code)]
383        struct User {
384            id: i64,
385            name: String,
386        }
387
388        #[derive(Clone)]
389        #[allow(dead_code)]
390        struct Order {
391            id: i64,
392            user: LazyRef<User>,
393        }
394
395        let user = Order {
396            id: 501,
397            user: LazyRef::new(|| User {
398                id: 1,
399                name: "alice".into(),
400            }),
401        };
402
403        assert!(!user.user.is_loaded());
404        let u = user.user.get().unwrap();
405        assert_eq!(u.name, "alice");
406        assert!(user.user.is_loaded());
407
408        user.user.invalidate();
409        assert!(!user.user.is_loaded());
410        let u2 = user.user.get().unwrap();
411        assert_eq!(u2.id, 1);
412    }
413
414    #[test]
415    fn test_e2e_lazy_loader_multi_entity_tracking() {
416        let loader = LazyLoader::new();
417
418        let lazy_profile: LazyRef<String> = loader.lazy(|| "alice profile".into());
419        let lazy_orders: LazyRef<Vec<i64>> = loader.lazy(|| vec![1, 2, 3]);
420
421        assert_eq!(loader.load_count(), 0);
422
423        lazy_profile.get();
424        assert_eq!(loader.load_count(), 1);
425
426        let _ = lazy_orders.get();
427        assert_eq!(loader.load_count(), 2);
428
429        lazy_profile.get();
430        let _ = lazy_orders.get();
431        assert_eq!(loader.load_count(), 2);
432
433        lazy_orders.invalidate();
434        let _ = lazy_orders.get();
435        assert_eq!(loader.load_count(), 3);
436    }
437}