mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
materialize the manytomany relationship to have better control over queries
This commit is contained in:
@@ -88,18 +88,6 @@ public class FeedDAO extends GenericDAO<Feed> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Feed findByIdWithEntries(Long feedId, int offset, int limit) {
|
||||
CriteriaQuery<Feed> query = builder.createQuery(getType());
|
||||
Root<Feed> root = query.from(getType());
|
||||
|
||||
query.where(builder.equal(root.get(Feed_.id), feedId));
|
||||
root.fetch(Feed_.entries, JoinType.LEFT);
|
||||
|
||||
TypedQuery<Feed> q = em.createQuery(query);
|
||||
limit(q, offset, limit);
|
||||
return q.getSingleResult();
|
||||
}
|
||||
|
||||
public List<Feed> findByTopic(String topic) {
|
||||
return findByField(Feed_.pushTopicHash, DigestUtils.sha1Hex(topic));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ import org.slf4j.LoggerFactory;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntry_;
|
||||
import com.commafeed.backend.model.Feed_;
|
||||
import com.commafeed.backend.model.FeedFeedEntry;
|
||||
import com.commafeed.backend.model.FeedFeedEntry_;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
@@ -33,11 +34,11 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||
|
||||
public static class EntryWithFeed {
|
||||
public FeedEntry entry;
|
||||
public Feed feed;
|
||||
public FeedFeedEntry ffe;
|
||||
|
||||
public EntryWithFeed(FeedEntry entry, Feed feed) {
|
||||
public EntryWithFeed(FeedEntry entry, FeedFeedEntry ffe) {
|
||||
this.entry = entry;
|
||||
this.feed = feed;
|
||||
this.ffe = ffe;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||
EntryWithFeed result = null;
|
||||
List<EntryWithFeed> list = q.getResultList();
|
||||
for (EntryWithFeed ewf : list) {
|
||||
if (ewf.entry != null && ewf.feed != null) {
|
||||
if (ewf.entry != null && ewf.ffe != null) {
|
||||
result = ewf;
|
||||
break;
|
||||
}
|
||||
@@ -66,9 +67,9 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||
public List<FeedEntry> findByFeed(Feed feed, int offset, int limit) {
|
||||
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
|
||||
Root<FeedEntry> root = query.from(getType());
|
||||
SetJoin<FeedEntry, Feed> feedsJoin = root.join(FeedEntry_.feeds);
|
||||
SetJoin<FeedEntry, FeedFeedEntry> feedsJoin = root.join(FeedEntry_.feedRelationships);
|
||||
|
||||
query.where(builder.equal(feedsJoin.get(Feed_.id), feed.getId()));
|
||||
query.where(builder.equal(feedsJoin.get(FeedFeedEntry_.feed), feed));
|
||||
query.orderBy(builder.desc(root.get(FeedEntry_.updated)));
|
||||
TypedQuery<FeedEntry> q = em.createQuery(query);
|
||||
limit(q, offset, limit);
|
||||
@@ -94,9 +95,9 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
|
||||
Root<FeedEntry> root = query.from(getType());
|
||||
|
||||
SetJoin<FeedEntry, Feed> join = root.join(FeedEntry_.feeds,
|
||||
SetJoin<FeedEntry, FeedFeedEntry> join = root.join(FeedEntry_.feedRelationships,
|
||||
JoinType.LEFT);
|
||||
query.where(builder.isNull(join.get(Feed_.id)));
|
||||
query.where(builder.isNull(join.get(FeedFeedEntry_.feed)));
|
||||
TypedQuery<FeedEntry> q = em.createQuery(query);
|
||||
q.setMaxResults(max);
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import javax.persistence.criteria.SetJoin;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -31,6 +30,8 @@ import com.commafeed.backend.model.FeedEntryContent_;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedEntryStatus_;
|
||||
import com.commafeed.backend.model.FeedEntry_;
|
||||
import com.commafeed.backend.model.FeedFeedEntry;
|
||||
import com.commafeed.backend.model.FeedFeedEntry_;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.FeedSubscription_;
|
||||
import com.commafeed.backend.model.Feed_;
|
||||
@@ -139,8 +140,8 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
CriteriaQuery<Tuple> query = builder.createTupleQuery();
|
||||
Root<FeedEntry> root = query.from(FeedEntry.class);
|
||||
|
||||
SetJoin<FeedEntry, Feed> feedJoin = root.join(FeedEntry_.feeds);
|
||||
SetJoin<Feed, FeedSubscription> subJoin = feedJoin
|
||||
Join<FeedFeedEntry, Feed> feedJoin = root.join(FeedEntry_.feedRelationships).join(FeedFeedEntry_.feed);
|
||||
Join<Feed, FeedSubscription> subJoin = feedJoin
|
||||
.join(Feed_.subscriptions);
|
||||
Join<FeedEntry, FeedEntryContent> contentJoin = root
|
||||
.join(FeedEntry_.content);
|
||||
@@ -231,8 +232,8 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
CriteriaQuery<Tuple> query = builder.createTupleQuery();
|
||||
Root<FeedEntry> root = query.from(FeedEntry.class);
|
||||
|
||||
SetJoin<FeedEntry, Feed> feedJoin = root.join(FeedEntry_.feeds);
|
||||
SetJoin<Feed, FeedSubscription> subJoin = feedJoin
|
||||
Join<FeedFeedEntry, Feed> feedJoin = root.join(FeedEntry_.feedRelationships).join(FeedFeedEntry_.feed);
|
||||
Join<Feed, FeedSubscription> subJoin = feedJoin
|
||||
.join(Feed_.subscriptions);
|
||||
|
||||
Selection<FeedEntry> entryAlias = root.alias("entry");
|
||||
@@ -317,8 +318,8 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
CriteriaQuery<FeedEntry> query = builder.createQuery(FeedEntry.class);
|
||||
Root<FeedEntry> root = query.from(FeedEntry.class);
|
||||
|
||||
SetJoin<FeedEntry, Feed> feedJoin = root.join(FeedEntry_.feeds);
|
||||
SetJoin<Feed, FeedSubscription> subJoin = feedJoin
|
||||
Join<FeedFeedEntry, Feed> feedJoin = root.join(FeedEntry_.feedRelationships).join(FeedFeedEntry_.feed);
|
||||
Join<Feed, FeedSubscription> subJoin = feedJoin
|
||||
.join(Feed_.subscriptions);
|
||||
|
||||
List<Predicate> predicates = Lists.newArrayList();
|
||||
@@ -388,8 +389,8 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
||||
CriteriaQuery<Tuple> query = builder.createTupleQuery();
|
||||
Root<FeedEntry> root = query.from(FeedEntry.class);
|
||||
|
||||
SetJoin<FeedEntry, Feed> feedJoin = root.join(FeedEntry_.feeds);
|
||||
SetJoin<Feed, FeedSubscription> subJoin = feedJoin
|
||||
Join<FeedFeedEntry, Feed> feedJoin = root.join(FeedEntry_.feedRelationships).join(FeedFeedEntry_.feed);
|
||||
Join<Feed, FeedSubscription> subJoin = feedJoin
|
||||
.join(Feed_.subscriptions);
|
||||
|
||||
Selection<FeedEntry> entryAlias = root.alias("entry");
|
||||
|
||||
@@ -4,11 +4,10 @@ import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.PreRemove;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
@@ -17,8 +16,6 @@ import javax.persistence.Transient;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FEEDS")
|
||||
@SuppressWarnings("serial")
|
||||
@@ -111,8 +108,8 @@ public class Feed extends AbstractModel {
|
||||
@Column(length = 40)
|
||||
private String lastContentHash;
|
||||
|
||||
@ManyToMany(mappedBy = "feeds")
|
||||
private Set<FeedEntry> entries = Sets.newHashSet();
|
||||
@OneToMany(mappedBy = "feed", cascade = CascadeType.REMOVE)
|
||||
private Set<FeedFeedEntry> entryRelationships;
|
||||
|
||||
@OneToMany(mappedBy = "feed")
|
||||
private Set<FeedSubscription> subscriptions;
|
||||
@@ -145,13 +142,6 @@ public class Feed extends AbstractModel {
|
||||
@Transient
|
||||
private boolean urgent;
|
||||
|
||||
@PreRemove
|
||||
private void removeEntriesFromFeed() {
|
||||
for (FeedEntry entry : entries) {
|
||||
entry.getFeeds().remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Feed() {
|
||||
|
||||
}
|
||||
@@ -176,14 +166,6 @@ public class Feed extends AbstractModel {
|
||||
this.lastUpdated = lastUpdated;
|
||||
}
|
||||
|
||||
public Set<FeedEntry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public void setEntries(Set<FeedEntry> entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
@@ -344,4 +326,12 @@ public class Feed extends AbstractModel {
|
||||
this.normalizedUrlHash = normalizedUrlHash;
|
||||
}
|
||||
|
||||
public Set<FeedFeedEntry> getEntryRelationships() {
|
||||
return entryRelationships;
|
||||
}
|
||||
|
||||
public void setEntryRelationships(Set<FeedFeedEntry> entryRelationships) {
|
||||
this.entryRelationships = entryRelationships;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
@@ -20,8 +18,6 @@ import javax.persistence.TemporalType;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
import com.google.api.client.util.Sets;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FEEDENTRIES")
|
||||
@SuppressWarnings("serial")
|
||||
@@ -35,9 +31,8 @@ public class FeedEntry extends AbstractModel {
|
||||
@Column(length = 40, nullable = false)
|
||||
private String guidHash;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(name = "FEED_FEEDENTRIES", joinColumns = { @JoinColumn(name = "FEEDENTRY_ID", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "FEED_ID", nullable = false, updatable = false) })
|
||||
private Set<Feed> feeds = Sets.newHashSet();
|
||||
@OneToMany(mappedBy = "entry", cascade = CascadeType.REMOVE)
|
||||
private Set<FeedFeedEntry> feedRelationships;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(nullable = false, updatable = false)
|
||||
@@ -82,14 +77,6 @@ public class FeedEntry extends AbstractModel {
|
||||
this.updated = updated;
|
||||
}
|
||||
|
||||
public Set<Feed> getFeeds() {
|
||||
return feeds;
|
||||
}
|
||||
|
||||
public void setFeeds(Set<Feed> feeds) {
|
||||
this.feeds = feeds;
|
||||
}
|
||||
|
||||
public Set<FeedEntryStatus> getStatuses() {
|
||||
return statuses;
|
||||
}
|
||||
@@ -130,4 +117,12 @@ public class FeedEntry extends AbstractModel {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public Set<FeedFeedEntry> getFeedRelationships() {
|
||||
return feedRelationships;
|
||||
}
|
||||
|
||||
public void setFeedRelationships(Set<FeedFeedEntry> feedRelationships) {
|
||||
this.feedRelationships = feedRelationships;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
57
src/main/java/com/commafeed/backend/model/FeedFeedEntry.java
Normal file
57
src/main/java/com/commafeed/backend/model/FeedFeedEntry.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
@Entity
|
||||
@Table(name = "FEED_FEEDENTRIES")
|
||||
@SuppressWarnings("serial")
|
||||
@Cacheable
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
|
||||
public class FeedFeedEntry implements Serializable {
|
||||
|
||||
@Id
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "FEED_ID")
|
||||
private Feed feed;
|
||||
|
||||
@Id
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "FEEDENTRY_ID")
|
||||
private FeedEntry entry;
|
||||
|
||||
public FeedFeedEntry() {
|
||||
|
||||
}
|
||||
|
||||
public FeedFeedEntry(Feed feed, FeedEntry entry) {
|
||||
this.feed = feed;
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public Feed getFeed() {
|
||||
return feed;
|
||||
}
|
||||
|
||||
public void setFeed(Feed feed) {
|
||||
this.feed = feed;
|
||||
}
|
||||
|
||||
public FeedEntry getEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void setEntry(FeedEntry entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import java.util.List;
|
||||
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import com.commafeed.backend.MetricsBean;
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
@@ -17,12 +19,16 @@ import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedFeedEntry;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Stateless
|
||||
public class FeedUpdateService {
|
||||
|
||||
@PersistenceContext
|
||||
protected EntityManager em;
|
||||
|
||||
@Inject
|
||||
FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
@@ -46,6 +52,7 @@ public class FeedUpdateService {
|
||||
entry.getUrl(), feed.getId());
|
||||
|
||||
FeedEntry update = null;
|
||||
FeedFeedEntry ffe = null;
|
||||
if (existing == null) {
|
||||
entry.setAuthor(FeedUtils.truncate(FeedUtils.handleContent(
|
||||
entry.getAuthor(), feed.getLink(), true), 128));
|
||||
@@ -56,11 +63,11 @@ public class FeedUpdateService {
|
||||
feed.getLink(), false));
|
||||
|
||||
entry.setInserted(new Date());
|
||||
entry.getFeeds().add(feed);
|
||||
ffe = new FeedFeedEntry(feed, entry);
|
||||
|
||||
update = entry;
|
||||
} else if (existing.feed == null) {
|
||||
existing.entry.getFeeds().add(feed);
|
||||
} else if (existing.ffe == null) {
|
||||
ffe = new FeedFeedEntry(feed, existing.entry);
|
||||
update = existing.entry;
|
||||
}
|
||||
|
||||
@@ -78,6 +85,7 @@ public class FeedUpdateService {
|
||||
cache.invalidateUserData(users.toArray(new User[0]));
|
||||
feedEntryDAO.saveOrUpdate(update);
|
||||
feedEntryStatusDAO.saveOrUpdate(statusUpdateList);
|
||||
em.persist(ffe);
|
||||
metricsBean.entryUpdated(statusUpdateList.size());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user