如何跟踪每个用户查看的项目

时间:2013-11-28 03:58:28

标签: java hibernate hibernate-mapping

我有productcategories的列表,需要知道每个用户是否已查看过这些内容,以及每个用户products的{​​{1}}总数每个用户。

简而言之,我需要保留

列表
  • 每位用户查看过的产品(一旦用户查看了产品的个人资料,就会将其添加到此列表中)
  • 每个用户查看的类别(一旦用户查看了类别的个人资料,它就会被添加到此列表中)
  • 每个类别中已查看过的产品数量 每个用户?

我已将两个成员变量添加到category类,如下所示。 我不确定它是否正确,其次我不知道如何为每个用户的每个类别的所有观看产品保留一个计数器。 (我知道我可以使用数据库的(COUNT)函数检索它,但我想知道是否有其他有效的方法,而不是将此请求发送到数据库)。

客户端:

Client

产品

@Entity
public class Client {
  @Id
  @GeneratedValue
  private long id;

  @OneToOne
  private List<Product> viewedProducts = new ArrayList<Product>(); //keep list of viewed products

  @OneToOne
  private List<Category> viewedCategories = new ArrayList<Category>(); //keep list of viewed categories

  ....
}

分类

@Entity
public class Product {

  @Id
  @GeneratedValue
  private long id;

  @ManyToOne(cascade = CascadeType.ALL)
  private Category category;

  .... 
}

4 个答案:

答案 0 :(得分:1)

我的建议是保留您目前拥有的成员变量,并将以下内容添加到您的班级

   @OneToMany
   private List<ViewedProductsPerCategory> = 
                                    new ArrayList<ViewedProductsPerCategory>();

在那个班级你应该

@Entity
public class ViewedProductsPerCategory {
   @Id
   @GeneratedValue
   long id;

   @OneToMany 
   Category category;

   long CatCounter;

   long ProCounter;

   getters and setters;
}

但是每次用户访问产品或类别时,请确保将计数器保留在cookie中,而不是调用数据库。用户注销后,将cookie中的数字添加到数据库中的相应计数器。

问题:请注意,用户可能会在退出之前删除cookie,但这种情况非常罕见,因为大多数用户在登录时不会删除其Cookie以避免被注销。

答案 1 :(得分:1)

首先,您应该对@ManyToManyviewedProducts使用viewedCategories注释。第二,有多少产品?如果数百或数千,您可能需要通过查看的产品进行分页,或者通过上下文或其他任何方式获取其中的一些。当您收集viewedProducts时,您将处理从数据库加载到内存中的所有已查看产品,这会导致性能下降。所以你最好创建这样的对象:

@Entity public ViewedProduct {
    @Id private Integer id;
    @ManyToOne
    private Client customer;
    @ManyToOne
    private Product product;

    ViewedProduct(Client customer, Product product) {
        this.customer = customer;
        this.product = product;
    }

    public Client getCustomer() {
         return customer;
    }

    public Product getProduct() {
         return product;
    }
}

@Entity public ViewedCategory {
    @Id private Integer id;
    @ManyToOne
    private Client customer;
    @ManyToOne
    private Category category;
    @Basic
    private int viewedProductCount;

    ViewedCategory(Client customer, Category category) {
        this.customer = customer;
        this.category = category;
    }

    void incrementViewedProducts() {
        viewedProductCount++;
    }

    public Client getCustomer() {
        return customer;
    }

    public Product getProduct() {
        return product;
    }
}

public class ProductViewService {
     private ViewedProductRepository viewedProductRepository;
     private ViewedCategoryRepository viewedCategoryRepository;

     public ProductViewService(ViewedProductRepository viewedProductRepository,
             ViewedCategoryRepository viewedCategoryRepository) {
         this.viewedProductRepository = viewedProductRepository;
         this.viewedCategoryRepository = viewedCategoryRepository;
     }

     public void viewProductByCustomer(Client customer, Product product) {
         ViewedProduct vp = viewedProductRepository.find(customer, product);
         if (vp == null) {
             vp = new ViewedProduct(customer, product);
             viewedProductRepository.add(vp);
             ViewedCategory vc = viewedCategoryRepository.find(customer, product.getCategory());
             if (vc == null) {
                 vc = new ViewedCategory(customer, product.getCategory());
                 viewedCategoryRepository.add(vc);
             }
             vc.incrementViewedProducts();
         }
     }
}

public interface ViewedProductRepository {
    ViewedProduct find(Client customer, Product product);
    void add(VIewedProduct product);
    List<Product> getRecentlyViewedProducts(Client customer, int limit);
    // etc
}

public class PersistentViewedProductRepository implements ViewedProductRepository {
    private EntityManager em;

    public List<Product> getRecentlyViewedProducts(Client customer, int limit) {
        return em.createQuery(
                "select vp.product " + 
                "from ViewedProduct vp " +
                "where vp.customer = :customer " +
                "order by vp.id desc", Product.class)
                .setMaxResults(limit)
                .getResultList();
    }

    // etc
}

计算你应该决定的产品,是否真的需要非规范化。如果确实没有,您可以丢弃viewedProductCount并使用JPQL查询来根据需要获取此数量。

答案 2 :(得分:0)

如果您的应用程序实际上不需要客户的产品视图数量,而是业务/营销部门需要衡量客户兴趣的指标,那么我认为您不应该将该逻辑放入您的应用程序中。有几种非侵入性的方法可以提供这些信息,而不会在每个页面视图上使用持续的DB写入来破坏您的应用程序。

根据日志记录的完成方式,您可以设置访问日志记录,如下所示:

2013-12-03 17:31:44 user:username@example.com INFO  com.acme.web.interceptors.AccessLogInterceptor  - URL [/product/1234]

然后使用日志分析工具(我的用户Heroku,其中包含Logentries附加组件)来收集您的商业智能信息。这也避免了您必须编写代码来显示/导出数据,这些都已经内置到工具中。

另一种选择是使用 Google Analytics ,它可以为您提供有关用户浏览模式,下线率等的更多详细信息。

答案 3 :(得分:0)

如果我们向域模型添加一些与模型中可能缺少的某些概念相对应的实体,这会使事情变得更简单。

如果这是一个营销/销售领域,最好为前两个点添加一些新的entities,如ProductViewEvent和CategoryViewEvent,这将指向Category和Product,并包含view事件的所有属性。

产品和类别本身不需要引用事件实体,它将是从事件实体到类别/产品的单向关系。

根据域,这些事件实体甚至可能成为value objects,两个视图事件是否可以从域视点完全互换?

对于第三点,添加一个新的service StatisticsService,它提供服务方法所需的分组总计。

对于运行总计,如果它导致在数据库上运行COUNTs的性能问题,则始终可以添加一个属性,其中包含类别/产品的运行总计视图。

但是在 update total = total + difference where ... 的服务层使用批量更新查询更新这些运行总计,以确保总数从数据库事务的角度保持一致性

除非必要,否则最好不要使用此优化,这取决于预期的视图量。你知道它是否在数千,数百万的范围内?