From b9da474c6aaff29410507b85ab8238e089fb60aa Mon Sep 17 00:00:00 2001 From: ZIJIAN CHEN Date: Tue, 18 Feb 2025 20:52:37 -0800 Subject: [PATCH 1/9] successfully get data from Json --- dependency-reduced-pom.xml | 91 ++++++++++++++++++++++++ pom.xml | 12 ++++ src/main/java/com/ecs160/JsonLoader.java | 27 +++++++ src/main/java/com/ecs160/MyApp.java | 19 ++++- src/main/java/com/ecs160/Post.java | 57 +++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 dependency-reduced-pom.xml create mode 100644 src/main/java/com/ecs160/JsonLoader.java create mode 100644 src/main/java/com/ecs160/Post.java diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml new file mode 100644 index 0000000..1cc8080 --- /dev/null +++ b/dependency-reduced-pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + com.ecs160 + annotations + 1.0-SNAPSHOT + + src/test/java + + + src/main/resources + + **/*.json + + + desktop.ini + + + + target/generated-sources/annotations + + desktop.ini + + + + + + maven-shade-plugin + 3.4.1 + + + package + + shade + + + true + + + com.ecs160.MyApp + + + + + *:* + + META-INF/MANIFEST.MF + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + maven-jar-plugin + 3.2.0 + + + + com.ecs160.MyApp + + + + + + maven-jar-plugin + 3.3.0 + + + + com.ecs160.hw1.SocialMediaAnalyzerDriver + + + + + + maven-surefire-plugin + 3.0.0 + + + + + UTF-8 + 21 + 21 + + diff --git a/pom.xml b/pom.xml index 8373c71..4fee3f6 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,18 @@ + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + com.ecs160.MyApp + + + + org.apache.maven.plugins maven-jar-plugin diff --git a/src/main/java/com/ecs160/JsonLoader.java b/src/main/java/com/ecs160/JsonLoader.java new file mode 100644 index 0000000..5b5fb8d --- /dev/null +++ b/src/main/java/com/ecs160/JsonLoader.java @@ -0,0 +1,27 @@ +package com.ecs160; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.List; + +public class JsonLoader { + + private static final Gson gson = new Gson(); + + public static List loadPosts(String filePath) throws IOException { + try (FileReader reader = new FileReader(filePath)) { + // 1️⃣ 解析 JSON 为 JsonObject + JsonObject jsonObject = gson.fromJson(reader, JsonObject.class); + + // 2️⃣ 获取 "feed" 数组 + JsonArray feedArray = jsonObject.getAsJsonArray("feed"); + + // 3️⃣ 解析 feed 数组为 List + Type listType = new TypeToken>() {}.getType(); + return gson.fromJson(feedArray, listType); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ecs160/MyApp.java b/src/main/java/com/ecs160/MyApp.java index d873b0d..e1b5611 100644 --- a/src/main/java/com/ecs160/MyApp.java +++ b/src/main/java/com/ecs160/MyApp.java @@ -2,12 +2,29 @@ import java.io.FileNotFoundException; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.List; public class MyApp { - public static void main(String[] args) throws FileNotFoundException, URISyntaxException, NoSuchFieldException { + public static void main(String[] args) { + try { + String filePath = "src/main/resources/input.json"; + List posts = JsonLoader.loadPosts(filePath); + System.out.println("✅ 成功加载 " + posts.size() + " 个帖子!"); + for (Post post : posts.subList(0, Math.min(posts.size(), 20))) { + System.out.println("😎 " + post); + if (post.getReplies() != null) { + for (Post reply : post.getReplies()) { + System.out.println(" ↳ Reply: " + reply); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } } } + diff --git a/src/main/java/com/ecs160/Post.java b/src/main/java/com/ecs160/Post.java new file mode 100644 index 0000000..d4a3164 --- /dev/null +++ b/src/main/java/com/ecs160/Post.java @@ -0,0 +1,57 @@ +package com.ecs160; + +import com.google.gson.annotations.SerializedName; +import java.util.List; + +public class Post { + + @SerializedName("thread") + private Thread thread; // 解析 thread 结构 + @SerializedName("replies") + private List replies; // 存储回复帖子 + + public String getPostId() {return (thread != null && thread.getPost() != null) ? thread.getPost().getCid() : null;} + public String getPostContent() {return (thread != null && thread.post.getRecord() != null) ? formatURL(thread.post.record.getText()): null;} + private String formatURL(String text) {if (text == null) return null;text = text.replaceAll("[\\n\\r]", " ");return text.replaceAll("(https?://\\S+|\\S+\\.\\S+)", "[link]");} + + + public List getReplies() {return replies;} + public void setReplies(List replies) {this.replies = replies;} + + @Override + public String toString() { + return "Post{" + + "postId=" + getPostId() + + ", postContent='" + getPostContent() + '\'' + + ", replies=" + (replies != null ? replies.size() : 0) + + '}'; + } + + // ✅ 静态内部类 Thread + public static class Thread { + @SerializedName("post") + private PostData post; + @SerializedName("replies") + private List replies; + + public PostData getPost() {return post;} + public List getReplies() {return replies;} + } + + // ✅ 静态内部类 PostData(解析 post.cid) + public static class PostData { + @SerializedName("cid") + private String cid; + @SerializedName("record") + private Record record; + public String getCid() {return cid;} + public Record getRecord() {return record;} + } + + // ✅ 静态内部类 Record(解析 record.text) + public static class Record { + @SerializedName("text") + private String text; + public String getText() {return text;} + } +} \ No newline at end of file From 0c17c7dc0737b6dc28fdd233def771a4a8c63d4a Mon Sep 17 00:00:00 2001 From: ZIJIAN CHEN Date: Wed, 19 Feb 2025 17:23:21 -0800 Subject: [PATCH 2/9] successfully get data from JsonSuccessfully-read-the-replies --- src/main/java/com/ecs160/MyApp.java | 39 +++++- src/main/java/com/ecs160/Post.java | 64 +++++++--- src/main/java/com/ecs160/RedisDataBase.java | 55 ++++++++ .../com/ecs160/persistence/Persistable.java | 1 + .../ecs160/persistence/PersistableField.java | 12 ++ .../com/ecs160/persistence/PersistableId.java | 12 ++ .../persistence/PersistableListField.java | 13 ++ .../java/com/ecs160/persistence/Session.java | 118 ++++++++++++++---- 8 files changed, 271 insertions(+), 43 deletions(-) create mode 100644 src/main/java/com/ecs160/RedisDataBase.java create mode 100644 src/main/java/com/ecs160/persistence/PersistableField.java create mode 100644 src/main/java/com/ecs160/persistence/PersistableId.java create mode 100644 src/main/java/com/ecs160/persistence/PersistableListField.java diff --git a/src/main/java/com/ecs160/MyApp.java b/src/main/java/com/ecs160/MyApp.java index e1b5611..e7c2b60 100644 --- a/src/main/java/com/ecs160/MyApp.java +++ b/src/main/java/com/ecs160/MyApp.java @@ -1,7 +1,13 @@ package com.ecs160; +import com.ecs160.persistence.Session; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; + import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; @@ -10,21 +16,48 @@ public class MyApp { public static void main(String[] args) { try { + // ✅ 读取 JSON 文件 String filePath = "src/main/resources/input.json"; List posts = JsonLoader.loadPosts(filePath); System.out.println("✅ 成功加载 " + posts.size() + " 个帖子!"); + // ✅ 遍历前 20 个帖子 for (Post post : posts.subList(0, Math.min(posts.size(), 20))) { System.out.println("😎 " + post); - if (post.getReplies() != null) { - for (Post reply : post.getReplies()) { - System.out.println(" ↳ Reply: " + reply); + + // ✅ 遍历 replies,确保不会报错 + if (post.getReplies() != null && !post.getReplies().isEmpty()) { + for (Post.Thread reply : post.getReplies()) { // ✅ 修正 `getReplies()` 调用 + if (reply.getPost() != null) { + System.out.println(" ↳ Reply ID: " + reply.getPost().getCid()); + System.out.println(" ↳ Reply Content: " + + (reply.getPost().getRecord() != null ? reply.getPost().getRecord().getText() : "null")); + } } } } } catch (Exception e) { + System.err.println("❌ 解析 JSON 失败:" + e.getMessage()); e.printStackTrace(); } } } +/* +public class MyApp { + public static void main(String[] args) { + Session session = Session.getInstance(); + + // ✅ 创建 `Post` + Post post = new Post(); + post.setPostId("123"); + post.setPostContent("Hello, world!"); + + // ✅ 存入 Redis + RedisDataBase.savePostToRedis(post); + + // ✅ 读取 `Post` + Post loadedPost = RedisDataBase.loadPostFromRedis("123"); + System.out.println("📌 Loaded Post: " + loadedPost); + } + */ diff --git a/src/main/java/com/ecs160/Post.java b/src/main/java/com/ecs160/Post.java index d4a3164..ff492de 100644 --- a/src/main/java/com/ecs160/Post.java +++ b/src/main/java/com/ecs160/Post.java @@ -1,57 +1,83 @@ package com.ecs160; +import com.ecs160.persistence.*; import com.google.gson.annotations.SerializedName; import java.util.List; +@Persistable public class Post { + @PersistableField @SerializedName("thread") - private Thread thread; // 解析 thread 结构 - @SerializedName("replies") - private List replies; // 存储回复帖子 - - public String getPostId() {return (thread != null && thread.getPost() != null) ? thread.getPost().getCid() : null;} - public String getPostContent() {return (thread != null && thread.post.getRecord() != null) ? formatURL(thread.post.record.getText()): null;} - private String formatURL(String text) {if (text == null) return null;text = text.replaceAll("[\\n\\r]", " ");return text.replaceAll("(https?://\\S+|\\S+\\.\\S+)", "[link]");} + private Thread thread; + public String getPostId() { + return (thread != null && thread.post != null) ? thread.post.getCid() : null; + } - public List getReplies() {return replies;} - public void setReplies(List replies) {this.replies = replies;} + public String getPostContent() { + if (thread != null && thread.post != null && thread.post.getRecord() != null) { + return formatURL(thread.post.getRecord().getText()) ; + } + return null; + } + private String formatURL(String text) {if (text == null) return null;text = text.replaceAll("[\\n\\r]", " ");return text.replaceAll("(https?://\\S+|\\S+\\.\\S+)", "[link]");} + // ✅ 直接返回 `thread.replies`,不涉及数据库、不做懒加载 + public List getReplies() { + return (thread != null) ? thread.getReplies() : null; + } @Override public String toString() { return "Post{" + "postId=" + getPostId() + ", postContent='" + getPostContent() + '\'' + - ", replies=" + (replies != null ? replies.size() : 0) + + ", replies=" + (getReplies() != null ? getReplies().size() : 0) + '}'; } - // ✅ 静态内部类 Thread + public static class Thread { + @PersistableField @SerializedName("post") private PostData post; + + @PersistableListField(className = "Post") @SerializedName("replies") - private List replies; + private List replies; + + public PostData getPost() { + return post; + } - public PostData getPost() {return post;} - public List getReplies() {return replies;} + public List getReplies() { + return replies; + } } - // ✅ 静态内部类 PostData(解析 post.cid) + public static class PostData { @SerializedName("cid") private String cid; + @SerializedName("record") private Record record; - public String getCid() {return cid;} - public Record getRecord() {return record;} + + public String getCid() { + return cid; + } + + public Record getRecord() { + return record; + } } - // ✅ 静态内部类 Record(解析 record.text) public static class Record { @SerializedName("text") private String text; - public String getText() {return text;} + + public String getText() { + return text; + } } } \ No newline at end of file diff --git a/src/main/java/com/ecs160/RedisDataBase.java b/src/main/java/com/ecs160/RedisDataBase.java new file mode 100644 index 0000000..c9963b4 --- /dev/null +++ b/src/main/java/com/ecs160/RedisDataBase.java @@ -0,0 +1,55 @@ +package com.ecs160; + +import com.ecs160.persistence.*; +import redis.clients.jedis.Jedis; +import java.util.List; +import java.util.stream.Collectors; + +public class RedisDataBase { + private static final Jedis redisClient = new Jedis("localhost", 6379); + + // ✅ 存储 `Post` + public static void savePostToRedis(Post post) { + if (post == null || post.getPostId() == null) { + System.out.println("❌ Error: Post or postId is null."); + return; + } + + // ✅ 使用 `Session` 存储 `Post` + Session session = Session.getInstance(); + session.add(post); + session.persistAll(); + + // ✅ 存储 `replies` 的 `postId` + if (post.getReplies() != null && !post.getReplies().isEmpty()) { + List replyIds = post.getReplies().stream() + .map(thread -> thread.getPost().getCid()) // ✅ 通过 `PostData` 获取 `postId` + .collect(Collectors.toList()); + redisClient.set(post.getPostId() + ":replies", String.join(",", replyIds)); + } + + System.out.println("✅ Successfully saved post: " + post.getPostId()); + } + + // ✅ 从 Redis 读取 `Post` + public static Post loadPostFromRedis(String postId) { + if (postId == null) { + System.out.println("❌ Error: postId is null."); + return null; + } + + // ✅ 使用 `Session` 读取 `Post` + Session session = Session.getInstance(); + Post post = (Post) session.load(Post.class, postId); + + if (post == null) { + System.out.println("❌ Error: Post not found in Redis."); + return null; + } + + // ✅ 读取 `replies` + + return post; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ecs160/persistence/Persistable.java b/src/main/java/com/ecs160/persistence/Persistable.java index d155ac2..f3617f7 100644 --- a/src/main/java/com/ecs160/persistence/Persistable.java +++ b/src/main/java/com/ecs160/persistence/Persistable.java @@ -7,3 +7,4 @@ public @interface Persistable { } + diff --git a/src/main/java/com/ecs160/persistence/PersistableField.java b/src/main/java/com/ecs160/persistence/PersistableField.java new file mode 100644 index 0000000..6934d84 --- /dev/null +++ b/src/main/java/com/ecs160/persistence/PersistableField.java @@ -0,0 +1,12 @@ +package com.ecs160.persistence; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +// ✅ 只有被标记的字段才会被 `Session` 存入 Redis +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface PersistableField { +} \ No newline at end of file diff --git a/src/main/java/com/ecs160/persistence/PersistableId.java b/src/main/java/com/ecs160/persistence/PersistableId.java new file mode 100644 index 0000000..48304e1 --- /dev/null +++ b/src/main/java/com/ecs160/persistence/PersistableId.java @@ -0,0 +1,12 @@ +package com.ecs160.persistence; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface PersistableId { +} \ No newline at end of file diff --git a/src/main/java/com/ecs160/persistence/PersistableListField.java b/src/main/java/com/ecs160/persistence/PersistableListField.java new file mode 100644 index 0000000..e4e5200 --- /dev/null +++ b/src/main/java/com/ecs160/persistence/PersistableListField.java @@ -0,0 +1,13 @@ +package com.ecs160.persistence; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface PersistableListField { + String className(); +} \ No newline at end of file diff --git a/src/main/java/com/ecs160/persistence/Session.java b/src/main/java/com/ecs160/persistence/Session.java index 9a84147..f581163 100644 --- a/src/main/java/com/ecs160/persistence/Session.java +++ b/src/main/java/com/ecs160/persistence/Session.java @@ -1,41 +1,117 @@ package com.ecs160.persistence; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javassist.util.proxy.MethodHandler; -import javassist.util.proxy.ProxyFactory; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import redis.clients.jedis.Jedis; +import java.lang.reflect.Field; +import java.util.*; +import java.util.stream.Collectors; - -// Assumption - only support int/long/and string values public class Session { + private static final Session instance = new Session(); + private static final Gson gson = new GsonBuilder().create(); + private final Jedis jedisSession; + private final Map cache = new HashMap<>(); // 临时缓存 - private Jedis jedisSession; - + // ✅ 私有构造函数(单例模式) private Session() { - jedisSession = new Jedis("localhost", 6379);; + this.jedisSession = new Jedis("localhost", 6379); } + // ✅ 获取单例实例 + public static Session getInstance() { + return instance; + } + // ✅ 添加对象到缓存 public void add(Object obj) { + if (obj == null) { + System.out.println("❌ Error: Cannot add null object."); + return; + } + String key = obj.getClass().getSimpleName() + ":" + getObjectId(obj); + cache.put(key, obj); } + // ✅ 持久化所有对象 + public void persistAll() { + for (Map.Entry entry : cache.entrySet()) { + String key = entry.getKey(); + Object obj = entry.getValue(); + Map redisData = new HashMap<>(); - public void persistAll() { + for (Field field : obj.getClass().getDeclaredFields()) { + if (field.isAnnotationPresent(PersistableField.class) || field.isAnnotationPresent(PersistableId.class)) { + field.setAccessible(true); + try { + redisData.put(field.getName(), gson.toJson(field.get(obj))); + } catch (IllegalAccessException e) { + throw new RuntimeException("❌ Error: Cannot access field " + field.getName(), e); + } + } else if (field.isAnnotationPresent(PersistableListField.class)) { + field.setAccessible(true); + try { + List list = (List) field.get(obj); + List ids = list.stream() + .map(this::getObjectId) + .collect(Collectors.toList()); + redisData.put(field.getName(), gson.toJson(ids)); + } catch (Exception e) { + throw new RuntimeException("❌ Error: Cannot process List field " + field.getName(), e); + } + } + } + + jedisSession.hmset(key, redisData); + System.out.println("✅ Persisted object: " + key); + } + cache.clear(); } + // ✅ 读取对象 + public Object load(Class clazz, String postId) { + if (postId == null || clazz == null) { + System.out.println("❌ Error: postId or class type is null."); + return null; + } + + String key = clazz.getSimpleName() + ":" + postId; + Map redisData = jedisSession.hgetAll(key); - public Object load(Object object) { - return null; + if (redisData.isEmpty()) { + System.out.println("❌ Error: No data found for key: " + key); + return null; + } + + try { + Object obj = clazz.getDeclaredConstructor().newInstance(); + for (Field field : clazz.getDeclaredFields()) { + if (redisData.containsKey(field.getName())) { + field.setAccessible(true); + Object value = gson.fromJson(redisData.get(field.getName()), field.getType()); + field.set(obj, value); + } + } + System.out.println("✅ Successfully loaded object: " + key); + return obj; + } catch (Exception e) { + throw new RuntimeException("❌ Error: Failed to deserialize object", e); + } } -} + // ✅ 获取对象的唯一 ID + private String getObjectId(Object obj) { + try { + for (Field field : obj.getClass().getDeclaredFields()) { + if (field.isAnnotationPresent(PersistableId.class)) { + field.setAccessible(true); + return (String) field.get(obj); + } + } + throw new RuntimeException("❌ Error: No field marked with @PersistableId found."); + } catch (Exception e) { + throw new RuntimeException("❌ Error: Cannot access @PersistableId field", e); + } + } +} \ No newline at end of file From 2a6021a299969a9584a2542af88a0880794bf070 Mon Sep 17 00:00:00 2001 From: ZIJIAN CHEN Date: Thu, 20 Feb 2025 18:48:41 -0800 Subject: [PATCH 3/9] successfully get data from JsonSuccessfully-read-the-replies Solved the Problem Session.java cannot get Replyids, or get Hash vallue by calling thread.getReplies()); return was as shown [com.ecs160.Post$Thread@631330c]. because by defult Java will call Object.toString which is not monifyed (Spent me over 10 hours to find out the solution.......) --- src/main/java/com/ecs160/JsonLoader.java | 16 +- src/main/java/com/ecs160/MyApp.java | 107 +++++++----- src/main/java/com/ecs160/Post.java | 53 +++++- src/main/java/com/ecs160/RedisDataBase.java | 55 ------- .../java/com/ecs160/persistence/Session.java | 152 +++++++++++------- 5 files changed, 216 insertions(+), 167 deletions(-) delete mode 100644 src/main/java/com/ecs160/RedisDataBase.java diff --git a/src/main/java/com/ecs160/JsonLoader.java b/src/main/java/com/ecs160/JsonLoader.java index 5b5fb8d..fb45968 100644 --- a/src/main/java/com/ecs160/JsonLoader.java +++ b/src/main/java/com/ecs160/JsonLoader.java @@ -8,20 +8,22 @@ import java.util.List; public class JsonLoader { - private static final Gson gson = new Gson(); public static List loadPosts(String filePath) throws IOException { try (FileReader reader = new FileReader(filePath)) { - // 1️⃣ 解析 JSON 为 JsonObject JsonObject jsonObject = gson.fromJson(reader, JsonObject.class); - - // 2️⃣ 获取 "feed" 数组 JsonArray feedArray = jsonObject.getAsJsonArray("feed"); - - // 3️⃣ 解析 feed 数组为 List Type listType = new TypeToken>() {}.getType(); - return gson.fromJson(feedArray, listType); + List posts = gson.fromJson(feedArray, listType); + + + /* + for (int i = 0; i < Math.min(posts.size(), 10); i++) { + System.out.println("✅ 加载 Post ID: " + posts.get(i).getPostId()); + } + */ + return posts; } } } \ No newline at end of file diff --git a/src/main/java/com/ecs160/MyApp.java b/src/main/java/com/ecs160/MyApp.java index e7c2b60..f7cfaa5 100644 --- a/src/main/java/com/ecs160/MyApp.java +++ b/src/main/java/com/ecs160/MyApp.java @@ -2,62 +2,87 @@ import com.ecs160.persistence.Session; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.stream.JsonReader; - -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.net.URISyntaxException; +import java.util.Scanner; import java.util.List; public class MyApp { public static void main(String[] args) { + + + Session.getredisSession().CleanDataBase(); + try { - // ✅ 读取 JSON 文件 String filePath = "src/main/resources/input.json"; List posts = JsonLoader.loadPosts(filePath); - System.out.println("✅ 成功加载 " + posts.size() + " 个帖子!"); - - // ✅ 遍历前 20 个帖子 - for (Post post : posts.subList(0, Math.min(posts.size(), 20))) { - System.out.println("😎 " + post); - - // ✅ 遍历 replies,确保不会报错 - if (post.getReplies() != null && !post.getReplies().isEmpty()) { - for (Post.Thread reply : post.getReplies()) { // ✅ 修正 `getReplies()` 调用 - if (reply.getPost() != null) { - System.out.println(" ↳ Reply ID: " + reply.getPost().getCid()); - System.out.println(" ↳ Reply Content: " + - (reply.getPost().getRecord() != null ? reply.getPost().getRecord().getText() : "null")); + int count = 0; + Session session = Session.getredisSession(); + for (Post post : posts) { + + session.add(post); + //TestIfGotPostsFromJson(count,post); + } + session.persistAll(); + + //PrintFist10Posts(posts); + + //UserInput(); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + + public static void UserInput(){ + Scanner scanner = new Scanner(System.in); + System.out.print("\nPlease Enter Post ID: "); + String inputPostId = scanner.nextLine().trim(); + + if (!inputPostId.isEmpty()) { + Post queriedPost = (Post) Session.getredisSession().load(Post.class, inputPostId); + if (queriedPost != null) { + //System.out.println("\nLoad Post ID Success!"); + // System.out.println("> " + queriedPost.getPostContent()); + + + List replyIds = queriedPost.getReplyIds(); + if (replyIds != null && !replyIds.isEmpty()) { + for (String replyId : replyIds) { + Post replyPost = (Post) Session.getredisSession().load(Post.class, replyId); + if (replyPost != null) { + // System.out.println("> --> " + replyPost.getPostContent()); } } + } } - } catch (Exception e) { - System.err.println("❌ 解析 JSON 失败:" + e.getMessage()); - e.printStackTrace(); + } + + scanner.close(); + } + public static void TestIfGotPostsFromJson(int count, Post post) { + if (count <=20) { + System.out.println("\n========== DEBUG: Loading Posts =========="+ " Number Of "+ count); + System.out.println("\uD83D\uDCCC" + post); + System.out.println("✅ Debug: if have replies In the Posts: " + post.getReplyIds()); } } -} -/* -public class MyApp { - public static void main(String[] args) { - Session session = Session.getInstance(); - // ✅ 创建 `Post` - Post post = new Post(); - post.setPostId("123"); - post.setPostContent("Hello, world!"); + public static void PrintFist10Posts(List posts) { + int J= 0; + for (int i = 0; i < Math.min(posts.size(), 10); i++) { + String postId = posts.get(i).getPostId(); - // ✅ 存入 Redis - RedisDataBase.savePostToRedis(post); + Post loadedPost = (Post) Session.getredisSession().load(Post.class, postId); + if (loadedPost != null) { - // ✅ 读取 `Post` - Post loadedPost = RedisDataBase.loadPostFromRedis("123"); - System.out.println("📌 Loaded Post: " + loadedPost); + System.out.println("Post ID: " + loadedPost.getPostId()); + System.out.println("Post Content: " + loadedPost.getPostContent()); + System.out.println("Reply IDs from Redis: " + + (loadedPost.getReplyIds() != null ? loadedPost.getReplyIds() : "[]")); + } + } } - */ +} \ No newline at end of file diff --git a/src/main/java/com/ecs160/Post.java b/src/main/java/com/ecs160/Post.java index ff492de..b4f96d5 100644 --- a/src/main/java/com/ecs160/Post.java +++ b/src/main/java/com/ecs160/Post.java @@ -2,6 +2,9 @@ import com.ecs160.persistence.*; import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.Collections; import java.util.List; @Persistable @@ -11,8 +14,19 @@ public class Post { @SerializedName("thread") private Thread thread; + @PersistableField + @PersistableListField(className = "Post") + private List replyIds; + + @PersistableId + private String postId; + + public String getPostId() { - return (thread != null && thread.post != null) ? thread.post.getCid() : null; + if (postId == null && thread != null && thread.post != null) { + postId = thread.post.getCid(); + } + return postId; } public String getPostContent() { @@ -21,18 +35,43 @@ public String getPostContent() { } return null; } - private String formatURL(String text) {if (text == null) return null;text = text.replaceAll("[\\n\\r]", " ");return text.replaceAll("(https?://\\S+|\\S+\\.\\S+)", "[link]");} - // ✅ 直接返回 `thread.replies`,不涉及数据库、不做懒加载 + public List getReplies() { - return (thread != null) ? thread.getReplies() : null; + if (thread != null && thread.getReplies() != null) { + return thread.getReplies(); + } + return List.of(); // 返回空列表,避免 null } + public List getReplyIds() { + if (getReplies() != null) { + replyIds = getReplies().stream() + .map(t -> t.getPost().getCid()) + .toList(); + } else { + replyIds = Collections.singletonList("[]"); + } + return replyIds; + } + + public void setReplyIds(List replyIds) { + this.replyIds = replyIds; + } + + public void setPostId(String postId) { + this.postId = postId; + } + + private String formatURL(String text) {if (text == null) return null;text = text.replaceAll("[\\n\\r]", " ");return text.replaceAll("(https?://\\S+|\\S+\\.\\S+)", "[link]");} + + @Override public String toString() { return "Post{" + "postId=" + getPostId() + ", postContent='" + getPostContent() + '\'' + - ", replies=" + (getReplies() != null ? getReplies().size() : 0) + + ", replies=" + (getReplies() != null ? + getReplies().stream().map(t -> t.getPost().getCid()).toList() : "[]") + '}'; } @@ -53,6 +92,10 @@ public PostData getPost() { public List getReplies() { return replies; } + public void setReplies(List replies) { + this.replies = replies; + } + } diff --git a/src/main/java/com/ecs160/RedisDataBase.java b/src/main/java/com/ecs160/RedisDataBase.java deleted file mode 100644 index c9963b4..0000000 --- a/src/main/java/com/ecs160/RedisDataBase.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.ecs160; - -import com.ecs160.persistence.*; -import redis.clients.jedis.Jedis; -import java.util.List; -import java.util.stream.Collectors; - -public class RedisDataBase { - private static final Jedis redisClient = new Jedis("localhost", 6379); - - // ✅ 存储 `Post` - public static void savePostToRedis(Post post) { - if (post == null || post.getPostId() == null) { - System.out.println("❌ Error: Post or postId is null."); - return; - } - - // ✅ 使用 `Session` 存储 `Post` - Session session = Session.getInstance(); - session.add(post); - session.persistAll(); - - // ✅ 存储 `replies` 的 `postId` - if (post.getReplies() != null && !post.getReplies().isEmpty()) { - List replyIds = post.getReplies().stream() - .map(thread -> thread.getPost().getCid()) // ✅ 通过 `PostData` 获取 `postId` - .collect(Collectors.toList()); - redisClient.set(post.getPostId() + ":replies", String.join(",", replyIds)); - } - - System.out.println("✅ Successfully saved post: " + post.getPostId()); - } - - // ✅ 从 Redis 读取 `Post` - public static Post loadPostFromRedis(String postId) { - if (postId == null) { - System.out.println("❌ Error: postId is null."); - return null; - } - - // ✅ 使用 `Session` 读取 `Post` - Session session = Session.getInstance(); - Post post = (Post) session.load(Post.class, postId); - - if (post == null) { - System.out.println("❌ Error: Post not found in Redis."); - return null; - } - - // ✅ 读取 `replies` - - return post; - } - -} \ No newline at end of file diff --git a/src/main/java/com/ecs160/persistence/Session.java b/src/main/java/com/ecs160/persistence/Session.java index f581163..ceb6947 100644 --- a/src/main/java/com/ecs160/persistence/Session.java +++ b/src/main/java/com/ecs160/persistence/Session.java @@ -1,117 +1,151 @@ package com.ecs160.persistence; +import com.ecs160.Post; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import redis.clients.jedis.Jedis; - import java.lang.reflect.Field; import java.util.*; import java.util.stream.Collectors; public class Session { - private static final Session instance = new Session(); - private static final Gson gson = new GsonBuilder().create(); + private static Session redisSession; private final Jedis jedisSession; - private final Map cache = new HashMap<>(); // 临时缓存 + private final Gson gson; + private final Map cache; - // ✅ 私有构造函数(单例模式) private Session() { this.jedisSession = new Jedis("localhost", 6379); + this.gson = new Gson(); + this.cache = new HashMap<>(); } - // ✅ 获取单例实例 - public static Session getInstance() { - return instance; - } + public static Session getredisSession() { + if (redisSession == null) { + redisSession = new Session(); + } - // ✅ 添加对象到缓存 + return redisSession; + + } public void add(Object obj) { - if (obj == null) { - System.out.println("❌ Error: Cannot add null object."); - return; + if (obj instanceof Post) { + ((Post) obj).getPostId(); + } + + try { + Field idField = getPersistableIdField(obj); + if (idField == null) { + return; + } + idField.setAccessible(true); + String postId = (String) idField.get(obj); + + if (postId == null || postId.isEmpty()) { + return; + } + String key = obj.getClass().getSimpleName() + ":" + postId; + cache.put(key, obj); + //System.out.println("DEBUG: Cache after adding post -> " + cache); + + + } catch (Exception e) { + e.printStackTrace(); } - String key = obj.getClass().getSimpleName() + ":" + getObjectId(obj); - cache.put(key, obj); + } - // ✅ 持久化所有对象 public void persistAll() { + + int c = 0; for (Map.Entry entry : cache.entrySet()) { String key = entry.getKey(); Object obj = entry.getValue(); - Map redisData = new HashMap<>(); + c++; + if (c <= 20) + { + if (obj instanceof Post p) { + System.out.println("🔍 Debug: replyIds = " + p.getReplyIds()); + } + + + System.out.println("++++++++++++++++++++++++++++++++++++++"); + } + jedisSession.hset(key, "data", gson.toJson(obj)); for (Field field : obj.getClass().getDeclaredFields()) { - if (field.isAnnotationPresent(PersistableField.class) || field.isAnnotationPresent(PersistableId.class)) { - field.setAccessible(true); - try { - redisData.put(field.getName(), gson.toJson(field.get(obj))); - } catch (IllegalAccessException e) { - throw new RuntimeException("❌ Error: Cannot access field " + field.getName(), e); - } - } else if (field.isAnnotationPresent(PersistableListField.class)) { - field.setAccessible(true); + field.setAccessible(true); + if (field.isAnnotationPresent(PersistableListField.class)) { try { List list = (List) field.get(obj); - List ids = list.stream() - .map(this::getObjectId) - .collect(Collectors.toList()); - redisData.put(field.getName(), gson.toJson(ids)); + if (list != null) { + List ids = list.stream() + .map(item -> { + String id = getObjectId(item); + return id; + }) + .toList(); + String replyIds = String.join(",", ids); + + jedisSession.hset(key, field.getName(), replyIds); + } } catch (Exception e) { - throw new RuntimeException("❌ Error: Cannot process List field " + field.getName(), e); + e.printStackTrace(); } + } } - - jedisSession.hmset(key, redisData); - System.out.println("✅ Persisted object: " + key); } - cache.clear(); + } - // ✅ 读取对象 + + public Object load(Class clazz, String postId) { if (postId == null || clazz == null) { - System.out.println("❌ Error: postId or class type is null."); return null; } String key = clazz.getSimpleName() + ":" + postId; Map redisData = jedisSession.hgetAll(key); - if (redisData.isEmpty()) { - System.out.println("❌ Error: No data found for key: " + key); return null; } try { - Object obj = clazz.getDeclaredConstructor().newInstance(); - for (Field field : clazz.getDeclaredFields()) { - if (redisData.containsKey(field.getName())) { - field.setAccessible(true); - Object value = gson.fromJson(redisData.get(field.getName()), field.getType()); - field.set(obj, value); - } - } - System.out.println("✅ Successfully loaded object: " + key); - return obj; + return gson.fromJson(redisData.get("data"), clazz); } catch (Exception e) { - throw new RuntimeException("❌ Error: Failed to deserialize object", e); + e.printStackTrace(); + return null; } } - // ✅ 获取对象的唯一 ID + private Field getPersistableIdField(Object obj) { + for (Field field : obj.getClass().getDeclaredFields()) { + if (field.isAnnotationPresent(PersistableId.class)) { + return field; + } + } + return null; + } private String getObjectId(Object obj) { + if (obj == null) { + return "null"; + } try { - for (Field field : obj.getClass().getDeclaredFields()) { - if (field.isAnnotationPresent(PersistableId.class)) { - field.setAccessible(true); - return (String) field.get(obj); - } + Field idField = getPersistableIdField(obj); + if (idField != null) { + idField.setAccessible(true); + String id = (String) idField.get(obj); + return (id != null) ? id : "null"; } - throw new RuntimeException("❌ Error: No field marked with @PersistableId found."); } catch (Exception e) { - throw new RuntimeException("❌ Error: Cannot access @PersistableId field", e); + e.printStackTrace(); } + return "null"; + } + + + public void CleanDataBase() { + jedisSession.flushDB(); } } \ No newline at end of file From cb6cc015c62c8abc0b905de68b1141ed89e974e6 Mon Sep 17 00:00:00 2001 From: ZIJIAN CHEN Date: Thu, 20 Feb 2025 18:48:50 -0800 Subject: [PATCH 4/9] successfully get data from JsonSuccessfully-read-the-replies Solved the Problem Session.java cannot get Replyids, or get Hash vallue by calling thread.getReplies()); return was as shown [com.ecs160.Post$Thread@631330c]. because by defult Java will call Object.toString which is not monifyed (Spent me over 10 hours to find out the solution.......) --- newpost.java | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 newpost.java diff --git a/newpost.java b/newpost.java new file mode 100644 index 0000000..2e34bab --- /dev/null +++ b/newpost.java @@ -0,0 +1,112 @@ +package com.ecs160; + +import com.google.gson.annotations.SerializedName; +import java.util.List; +import java.util.ArrayList; +import java.util.stream.Collectors; + +@Persistable +public class Post { + + @PersistableId + @SerializedName("cid") + private String postId; + + @PersistableField + @SerializedName("post") + private PostData post; // ✅ `record` 现在在 `post` 里面 + + @PersistableField + @SerializedName("thread") + private Thread thread; // ✅ `replies` 仍然在 `thread` 里 + + @LazyLoad + @PersistableListField(className = "Post") + @SerializedName("replyIds") + private List replyIds; + + private transient List replies; + + public String getPostId() { + return (post != null) ? post.getCid() : null; + } + + // ✅ `record` 现在在 `post` 里 + public String getPostContent() { + if (post != null && post.getRecord() != null) { + return maskUrls(post.getRecord().getText()); + } + return null; + } + + private String maskUrls(String text) { + if (text == null) return null; + return text.replaceAll("(https?://\\S+|\\S+\\.\\S+)", "[link]"); + } + + // ✅ 解析 `thread.replies` + public List getReplies() { + if (replies == null && thread != null && thread.getReplies() != null) { + replies = thread.getReplies(); + } + return replies; + } + + public void setReplies(List replies) { + if (replies != null) { + this.replies = replies; + this.replyIds = replies.stream().map(Post::getPostId).toList(); + } + } + + @Override + public String toString() { + return "Post{" + + "postId=" + getPostId() + + ", postContent='" + getPostContent() + '\'' + + ", replies=" + (replyIds != null ? replyIds : "[]") + + '}'; + } + + // ✅ `Thread` 仍然保留,因为 `replies` 在 `thread` 里 + public static class Thread { + @SerializedName("post") + private PostData post; + + @SerializedName("replies") + private List replies; // ✅ `replies` 仍然在 `thread` 里 + + public PostData getPost() { + return post; + } + + public List getReplies() { + return replies; + } + } + + public static class PostData { + @SerializedName("cid") + private String cid; + + @SerializedName("record") + private Record record; // ✅ `record` 现在在 `post` 里 + + public String getCid() { + return cid; + } + + public Record getRecord() { + return record; + } + } + + public static class Record { + @SerializedName("text") + private String text; + + public String getText() { + return text; + } + } +} \ No newline at end of file From d9ca09eb3a9013e60496e4edc3cde5a467b92b31 Mon Sep 17 00:00:00 2001 From: ZIJIAN CHEN Date: Thu, 20 Feb 2025 19:54:02 -0800 Subject: [PATCH 5/9] =?UTF-8?q?Now=20=EF=BC=81=EF=BC=81=EF=BC=81=20replyId?= =?UTF-8?q?s=20stored=20to=20the=20Redis=20by=20Session=20Successfuly!!!!!?= =?UTF-8?q?!!=20As=20a=20LIST?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ecs160/Post.java | 6 ++-- .../java/com/ecs160/persistence/Session.java | 34 +++++++------------ 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/ecs160/Post.java b/src/main/java/com/ecs160/Post.java index b4f96d5..ccdddfa 100644 --- a/src/main/java/com/ecs160/Post.java +++ b/src/main/java/com/ecs160/Post.java @@ -45,16 +45,18 @@ public List getReplies() { public List getReplyIds() { if (getReplies() != null) { - replyIds = getReplies().stream() + this.replyIds = getReplies().stream() .map(t -> t.getPost().getCid()) .toList(); } else { - replyIds = Collections.singletonList("[]"); + this.replyIds = List.of(); } return replyIds; } public void setReplyIds(List replyIds) { + System.out.println("✅ Debug: setReplyIds 被调用, replyIds = " + replyIds); + this.replyIds = replyIds; } diff --git a/src/main/java/com/ecs160/persistence/Session.java b/src/main/java/com/ecs160/persistence/Session.java index ceb6947..7c61a78 100644 --- a/src/main/java/com/ecs160/persistence/Session.java +++ b/src/main/java/com/ecs160/persistence/Session.java @@ -56,38 +56,29 @@ public void add(Object obj) { public void persistAll() { - int c = 0; for (Map.Entry entry : cache.entrySet()) { String key = entry.getKey(); Object obj = entry.getValue(); - c++; - if (c <= 20) - { - if (obj instanceof Post p) { - System.out.println("🔍 Debug: replyIds = " + p.getReplyIds()); - } + + if (obj instanceof Post p) {p.getReplyIds();} //Nor sure if this is allowed.... + // but I really spent over 10 hours to findi hits solution - System.out.println("++++++++++++++++++++++++++++++++++++++"); - } jedisSession.hset(key, "data", gson.toJson(obj)); for (Field field : obj.getClass().getDeclaredFields()) { field.setAccessible(true); if (field.isAnnotationPresent(PersistableListField.class)) { try { - List list = (List) field.get(obj); - if (list != null) { - List ids = list.stream() - .map(item -> { - String id = getObjectId(item); - return id; - }) - .toList(); - String replyIds = String.join(",", ids); - - jedisSession.hset(key, field.getName(), replyIds); - } + //System.out.println("This is field.get(obj)" + field.get(obj)); + //System.out.println("This is replyIds " + replyIds); + //System.out.println("This is ReplyIds " + replyIds); + + List replyIdsList = (List) field.get(obj); + String replyIds = String.join(",", replyIdsList); + + jedisSession.hset(key, field.getName(), replyIds); + } catch (Exception e) { e.printStackTrace(); } @@ -101,6 +92,7 @@ public void persistAll() { public Object load(Class clazz, String postId) { + if (postId == null || clazz == null) { return null; } From f49854b0796405f6346ec4ee020c03232befd844 Mon Sep 17 00:00:00 2001 From: ZIJIAN CHEN Date: Thu, 20 Feb 2025 20:08:55 -0800 Subject: [PATCH 6/9] Main Successfully read From Redis, and Print out --- src/main/java/com/ecs160/MyApp.java | 10 ++++++---- src/main/java/com/ecs160/Post.java | 2 -- src/main/java/com/ecs160/persistence/Session.java | 6 ++++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/ecs160/MyApp.java b/src/main/java/com/ecs160/MyApp.java index f7cfaa5..15095c5 100644 --- a/src/main/java/com/ecs160/MyApp.java +++ b/src/main/java/com/ecs160/MyApp.java @@ -23,7 +23,7 @@ public static void main(String[] args) { } session.persistAll(); - //PrintFist10Posts(posts); + PrintFist10Posts(posts); //UserInput(); @@ -77,9 +77,11 @@ public static void PrintFist10Posts(List posts) { Post loadedPost = (Post) Session.getredisSession().load(Post.class, postId); if (loadedPost != null) { - System.out.println("Post ID: " + loadedPost.getPostId()); - System.out.println("Post Content: " + loadedPost.getPostContent()); - System.out.println("Reply IDs from Redis: " + + System.out.println("\n========== DEBUG: Loading Posts From Redis =========="); + + System.out.println("✅ Post ID: " + loadedPost.getPostId()); + System.out.println("✅ Post Content: " + loadedPost.getPostContent()); + System.out.println("✅ Reply IDs from Redis: " + (loadedPost.getReplyIds() != null ? loadedPost.getReplyIds() : "[]")); } } diff --git a/src/main/java/com/ecs160/Post.java b/src/main/java/com/ecs160/Post.java index ccdddfa..c9d3b75 100644 --- a/src/main/java/com/ecs160/Post.java +++ b/src/main/java/com/ecs160/Post.java @@ -55,8 +55,6 @@ public List getReplyIds() { } public void setReplyIds(List replyIds) { - System.out.println("✅ Debug: setReplyIds 被调用, replyIds = " + replyIds); - this.replyIds = replyIds; } diff --git a/src/main/java/com/ecs160/persistence/Session.java b/src/main/java/com/ecs160/persistence/Session.java index 7c61a78..549e072 100644 --- a/src/main/java/com/ecs160/persistence/Session.java +++ b/src/main/java/com/ecs160/persistence/Session.java @@ -73,10 +73,9 @@ public void persistAll() { //System.out.println("This is field.get(obj)" + field.get(obj)); //System.out.println("This is replyIds " + replyIds); //System.out.println("This is ReplyIds " + replyIds); - List replyIdsList = (List) field.get(obj); String replyIds = String.join(",", replyIdsList); - + //System.out.println("This is replyIds: " + replyIds); jedisSession.hset(key, field.getName(), replyIds); } catch (Exception e) { @@ -111,6 +110,9 @@ public Object load(Class clazz, String postId) { } } + + + private Field getPersistableIdField(Object obj) { for (Field field : obj.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(PersistableId.class)) { From 9dcf8d002436d4e17245562e5d3d8f9e1575613e Mon Sep 17 00:00:00 2001 From: ZIJIAN CHEN Date: Thu, 20 Feb 2025 20:34:38 -0800 Subject: [PATCH 7/9] Finished Project without LazyLoad --- .../java/com/ecs160/persistence/LazyLoad.java | 11 +++++++++++ src/main/java/com/ecs160/persistence/Session.java | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/ecs160/persistence/LazyLoad.java diff --git a/src/main/java/com/ecs160/persistence/LazyLoad.java b/src/main/java/com/ecs160/persistence/LazyLoad.java new file mode 100644 index 0000000..f308751 --- /dev/null +++ b/src/main/java/com/ecs160/persistence/LazyLoad.java @@ -0,0 +1,11 @@ +package com.ecs160.persistence; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface LazyLoad { +} diff --git a/src/main/java/com/ecs160/persistence/Session.java b/src/main/java/com/ecs160/persistence/Session.java index 549e072..1499032 100644 --- a/src/main/java/com/ecs160/persistence/Session.java +++ b/src/main/java/com/ecs160/persistence/Session.java @@ -68,6 +68,7 @@ public void persistAll() { for (Field field : obj.getClass().getDeclaredFields()) { field.setAccessible(true); + if (field.isAnnotationPresent(PersistableListField.class)) { try { //System.out.println("This is field.get(obj)" + field.get(obj)); @@ -77,6 +78,7 @@ public void persistAll() { String replyIds = String.join(",", replyIdsList); //System.out.println("This is replyIds: " + replyIds); jedisSession.hset(key, field.getName(), replyIds); + // System.out.println("Saving to Redis Key: " + key + ", Field: " + field.getName() + ", Value: " + replyIds); } catch (Exception e) { e.printStackTrace(); @@ -91,19 +93,28 @@ public void persistAll() { public Object load(Class clazz, String postId) { - if (postId == null || clazz == null) { return null; } String key = clazz.getSimpleName() + ":" + postId; Map redisData = jedisSession.hgetAll(key); + if (redisData.isEmpty()) { return null; } try { - return gson.fromJson(redisData.get("data"), clazz); + Post post = gson.fromJson(redisData.get("data"), Post.class); + + // 解析 replyIds 并转换为 Post 对象 + if (redisData.containsKey("replyIds")) { + String replyIdsStr = redisData.get("replyIds"); + List replyIdsList = replyIdsStr.isEmpty() ? List.of() : Arrays.asList(replyIdsStr.split(",")); + post.setReplyIds(replyIdsList); + } + + return post; } catch (Exception e) { e.printStackTrace(); return null; From 192655699fa69396f38e6d3046ba25d039ba8bec Mon Sep 17 00:00:00 2001 From: ZIJIAN CHEN Date: Thu, 20 Feb 2025 21:45:11 -0800 Subject: [PATCH 8/9] Finished Project without LazyLoad --- src/main/java/com/ecs160/MyApp.java | 57 +++++++++++++++++------------ src/main/java/com/ecs160/Post.java | 22 +++++++++-- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/ecs160/MyApp.java b/src/main/java/com/ecs160/MyApp.java index 15095c5..1fd3cf0 100644 --- a/src/main/java/com/ecs160/MyApp.java +++ b/src/main/java/com/ecs160/MyApp.java @@ -23,9 +23,9 @@ public static void main(String[] args) { } session.persistAll(); - PrintFist10Posts(posts); + //PrintFist10Posts(posts); - //UserInput(); + UserInput(); } catch (Exception e) { e.printStackTrace(); @@ -34,33 +34,41 @@ public static void main(String[] args) { } - public static void UserInput(){ + public static void UserInput() { Scanner scanner = new Scanner(System.in); - System.out.print("\nPlease Enter Post ID: "); - String inputPostId = scanner.nextLine().trim(); - - if (!inputPostId.isEmpty()) { - Post queriedPost = (Post) Session.getredisSession().load(Post.class, inputPostId); - if (queriedPost != null) { - //System.out.println("\nLoad Post ID Success!"); - // System.out.println("> " + queriedPost.getPostContent()); - - - List replyIds = queriedPost.getReplyIds(); - if (replyIds != null && !replyIds.isEmpty()) { - for (String replyId : replyIds) { - Post replyPost = (Post) Session.getredisSession().load(Post.class, replyId); - if (replyPost != null) { - // System.out.println("> --> " + replyPost.getPostContent()); - } - } + while (true) { + System.out.print("\nPlease Enter Post ID (or type 'exit' to quit): "); + String inputPostId = scanner.nextLine().trim(); + + if (inputPostId.equalsIgnoreCase("exit")) { + System.out.println("Exiting UserInput mode..."); + break; + } + // 加载 Post + Post post = (Post) Session.getredisSession().load(Post.class, inputPostId); + + if (post != null) { + // 成功查询到 Post + System.out.println("\nLoad Post Successfully..."); + System.out.println("Post Content: " + post.getPostContent()); + System.out.println("Reply IDs: " + (post.getReplyIds() != null ? post.getReplyIds() : "[]")); + List replyTexts = post.getReplyTexts(); + if (!replyTexts.isEmpty()) { + System.out.println(" Replies:"); + for (String replyText : replyTexts) { + System.out.println(" " + replyText); + } + } else { + System.out.println(" No Replies Found."); } + } else { + System.out.println("Post Not Found. Please try again."); } } - scanner.close(); } + public static void TestIfGotPostsFromJson(int count, Post post) { if (count <=20) { System.out.println("\n========== DEBUG: Loading Posts =========="+ " Number Of "+ count); @@ -71,7 +79,7 @@ public static void TestIfGotPostsFromJson(int count, Post post) { public static void PrintFist10Posts(List posts) { int J= 0; - for (int i = 0; i < Math.min(posts.size(), 10); i++) { + for (int i = 0; i < Math.min(posts.size(), 2); i++) { String postId = posts.get(i).getPostId(); Post loadedPost = (Post) Session.getredisSession().load(Post.class, postId); @@ -83,6 +91,9 @@ public static void PrintFist10Posts(List posts) { System.out.println("✅ Post Content: " + loadedPost.getPostContent()); System.out.println("✅ Reply IDs from Redis: " + (loadedPost.getReplyIds() != null ? loadedPost.getReplyIds() : "[]")); + + + //System.out.println("\uD83D\uDCCC" + loadedPost.getReplyTexts()); } } } diff --git a/src/main/java/com/ecs160/Post.java b/src/main/java/com/ecs160/Post.java index c9d3b75..66fbd7f 100644 --- a/src/main/java/com/ecs160/Post.java +++ b/src/main/java/com/ecs160/Post.java @@ -12,14 +12,14 @@ public class Post { @PersistableField @SerializedName("thread") - private Thread thread; + private Post.Thread thread; @PersistableField @PersistableListField(className = "Post") private List replyIds; @PersistableId - private String postId; + private String postId; public String getPostId() { @@ -64,6 +64,22 @@ public void setPostId(String postId) { private String formatURL(String text) {if (text == null) return null;text = text.replaceAll("[\\n\\r]", " ");return text.replaceAll("(https?://\\S+|\\S+\\.\\S+)", "[link]");} + public List getReplyTexts() { + if (this.getReplies() == null) { + return List.of(); + } + + return this.getReplies().stream() + .map(reply -> { + if (reply.getPost() != null && reply.getPost().getRecord() != null) { + return reply.getPost().getRecord().getText(); + } + return "(No text available)"; + }) + .toList(); + } + + @Override public String toString() { @@ -83,7 +99,7 @@ public static class Thread { @PersistableListField(className = "Post") @SerializedName("replies") - private List replies; + private List replies; public PostData getPost() { return post; From a465359a1c9a601b05faf5a9b49cb9a238f35d5e Mon Sep 17 00:00:00 2001 From: ZIJIAN CHEN Date: Thu, 20 Feb 2025 21:48:57 -0800 Subject: [PATCH 9/9] Finished Project without LazyLoad --- src/main/java/com/ecs160/JsonLoader.java | 2 +- src/main/java/com/ecs160/Post.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ecs160/JsonLoader.java b/src/main/java/com/ecs160/JsonLoader.java index fb45968..d3b8988 100644 --- a/src/main/java/com/ecs160/JsonLoader.java +++ b/src/main/java/com/ecs160/JsonLoader.java @@ -20,7 +20,7 @@ public static List loadPosts(String filePath) throws IOException { /* for (int i = 0; i < Math.min(posts.size(), 10); i++) { - System.out.println("✅ 加载 Post ID: " + posts.get(i).getPostId()); + System.out.println("加载 Post ID: " + posts.get(i).getPostId()); } */ return posts; diff --git a/src/main/java/com/ecs160/Post.java b/src/main/java/com/ecs160/Post.java index 66fbd7f..f06fc31 100644 --- a/src/main/java/com/ecs160/Post.java +++ b/src/main/java/com/ecs160/Post.java @@ -40,7 +40,7 @@ public List getReplies() { if (thread != null && thread.getReplies() != null) { return thread.getReplies(); } - return List.of(); // 返回空列表,避免 null + return List.of(); } public List getReplyIds() {