using hibernate to fetch entries now, able to fetch entries in one go

This commit is contained in:
Jeremie Panzer
2013-03-26 16:02:26 +01:00
parent 654cee9a1f
commit 0d3a93cdf0
9 changed files with 159 additions and 81 deletions

27
pom.xml
View File

@@ -69,6 +69,17 @@
<updateOnlyExtension>.css</updateOnlyExtension> <updateOnlyExtension>.css</updateOnlyExtension>
</updateOnlyExtensions> </updateOnlyExtensions>
</synchronization> </synchronization>
<libs>
<lib>org.hibernate:hibernate-entitymanager:4.1.10.Final</lib>
<lib>org.hibernate:hibernate-core:4.1.10.Final</lib>
<lib>org.hibernate.common:hibernate-commons-annotations:4.0.1.Final</lib>
<lib>org.hibernate:hibernate-validator:4.3.1.Final</lib>
<lib>org.jboss.logging:jboss-logging:3.1.3.GA</lib>
<lib>dom4j:dom4j:1.6.1</lib>
<lib>antlr:antlr:2.7.7</lib>
<lib>remove:hsqldb</lib>
<lib>org.hsqldb:hsqldb:SNAPSHOT</lib>
</libs>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
@@ -82,6 +93,12 @@
<type>pom</type> <type>pom</type>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.10.Final</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>uaihebert.com</groupId> <groupId>uaihebert.com</groupId>
<artifactId>EasyCriteria</artifactId> <artifactId>EasyCriteria</artifactId>
@@ -211,6 +228,16 @@
<enabled>true</enabled> <enabled>true</enabled>
</snapshots> </snapshots>
</repository> </repository>
<repository>
<id>hsqldb.snapshots</id>
<url>http://www.hsqldb.org/repos/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories> </repositories>
<pluginRepositories> <pluginRepositories>
<pluginRepository> <pluginRepository>

View File

@@ -6,6 +6,7 @@ import java.util.List;
import javax.ejb.Stateless; import javax.ejb.Stateless;
import javax.inject.Inject; import javax.inject.Inject;
import javax.persistence.Query;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@@ -13,7 +14,9 @@ import org.apache.commons.lang.StringUtils;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedCategory; import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryStatus;
import com.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.commafeed.backend.model.extended.FeedEntryWithStatus;
import com.commafeed.frontend.utils.ModelFactory.MF; import com.commafeed.frontend.utils.ModelFactory.MF;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -62,47 +65,48 @@ public class FeedEntryService extends GenericDAO<FeedEntry, Long> {
return query.getResultList(); return query.getResultList();
} }
public List<FeedEntry> getEntries(Feed feed, User user, boolean read) { public List<FeedEntryWithStatus> getEntries(Feed feed, User user,
return getEntries(feed, user, read, -1, -1); boolean unreadOnly) {
return getEntries(feed, user, unreadOnly, -1, -1);
} }
public List<FeedEntry> getEntries(Feed feed, User user, boolean read, public List<FeedEntryWithStatus> getEntries(Feed feed, User user,
int offset, int limit) { boolean unreadOnly, int offset, int limit) {
String queryName = null; String queryName = null;
if (read) { if (unreadOnly) {
queryName = "Entry.readByFeed";
} else {
queryName = "Entry.unreadByFeed"; queryName = "Entry.unreadByFeed";
}
TypedQuery<FeedEntry> query = em.createNamedQuery(queryName,
FeedEntry.class);
query.setParameter("feed", feed);
query.setParameter("user", user);
if (offset > -1) {
query.setFirstResult(offset);
}
if (limit > -1) {
query.setMaxResults(limit);
}
return query.getResultList();
}
public List<FeedEntry> getEntries(List<FeedCategory> categories, User user,
boolean read) {
return getEntries(categories, user, read, -1, -1);
}
public List<FeedEntry> getEntries(List<FeedCategory> categories, User user,
boolean read, int offset, int limit) {
String queryName = null;
if (read) {
queryName = "Entry.readByCategories";
} else { } else {
queryName = "Entry.unreadByCategories"; queryName = "Entry.allByFeed";
} }
TypedQuery<FeedEntry> query = em.createNamedQuery(queryName, Query query = em.createNamedQuery(queryName);
FeedEntry.class); query.setParameter("feed", feed);
query.setParameter("userId", user.getId());
if (offset > -1) {
query.setFirstResult(offset);
}
if (limit > -1) {
query.setMaxResults(limit);
}
return buildList(query.getResultList());
}
public List<FeedEntryWithStatus> getEntries(List<FeedCategory> categories, User user,
boolean unreadOnly) {
return getEntries(categories, user, unreadOnly, -1, -1);
}
public List<FeedEntryWithStatus> getEntries(List<FeedCategory> categories, User user,
boolean unreadOnly, int offset, int limit) {
String queryName = null;
if (unreadOnly) {
queryName = "Entry.unreadByCategories";
} else {
queryName = "Entry.allByCategories";
}
Query query = em.createNamedQuery(queryName);
query.setParameter("categories", categories); query.setParameter("categories", categories);
query.setParameter("userId", user.getId());
query.setParameter("user", user); query.setParameter("user", user);
if (offset > -1) { if (offset > -1) {
query.setFirstResult(offset); query.setFirstResult(offset);
@@ -110,6 +114,18 @@ public class FeedEntryService extends GenericDAO<FeedEntry, Long> {
if (limit > -1) { if (limit > -1) {
query.setMaxResults(limit); query.setMaxResults(limit);
} }
return query.getResultList(); return buildList(query.getResultList());
}
@SuppressWarnings("rawtypes")
private List<FeedEntryWithStatus> buildList(List list){
List<FeedEntryWithStatus> result = Lists.newArrayList();
for (Object object :list) {
Object[] array = (Object[]) object;
FeedEntry entry = (FeedEntry) array[0];
FeedEntryStatus status = (FeedEntryStatus) array[1];
result.add(new FeedEntryWithStatus(entry, status));
}
return result;
} }
} }

View File

@@ -1,16 +1,20 @@
package com.commafeed.backend.model; package com.commafeed.backend.model;
import java.util.Date; import java.util.Date;
import java.util.Set;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.Lob; import javax.persistence.Lob;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Temporal; import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import org.apache.commons.codec.binary.StringUtils;
@Entity @Entity
@Table(name = "FEEDENTRIES") @Table(name = "FEEDENTRIES")
@SuppressWarnings("serial") @SuppressWarnings("serial")
@@ -26,7 +30,7 @@ public class FeedEntry extends AbstractModel {
private String title; private String title;
@Lob @Lob
private String content; private byte[] content;
@Column(length = 2048) @Column(length = 2048)
private String url; private String url;
@@ -34,6 +38,9 @@ public class FeedEntry extends AbstractModel {
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date updated; private Date updated;
@OneToMany(mappedBy = "entry")
private Set<FeedEntryStatus> statuses;
public String getGuid() { public String getGuid() {
return guid; return guid;
} }
@@ -51,11 +58,11 @@ public class FeedEntry extends AbstractModel {
} }
public String getContent() { public String getContent() {
return content; return StringUtils.newStringUtf8(content);
} }
public void setContent(String content) { public void setContent(String content) {
this.content = content; this.content = StringUtils.getBytesUtf8(content);
} }
public String getUrl() { public String getUrl() {
@@ -82,4 +89,12 @@ public class FeedEntry extends AbstractModel {
this.feed = feed; this.feed = feed;
} }
public Set<FeedEntryStatus> getStatuses() {
return statuses;
}
public void setStatuses(Set<FeedEntryStatus> statuses) {
this.statuses = statuses;
}
} }

View File

@@ -0,0 +1,31 @@
package com.commafeed.backend.model.extended;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryStatus;
public class FeedEntryWithStatus {
private FeedEntry entry;
private FeedEntryStatus status;
public FeedEntryWithStatus(FeedEntry entry, FeedEntryStatus status) {
this.entry = entry;
this.status = status;
}
public FeedEntry getEntry() {
return entry;
}
public void setEntry(FeedEntry entry) {
this.entry = entry;
}
public FeedEntryStatus getStatus() {
return status;
}
public void setStatus(FeedEntryStatus status) {
this.status = status;
}
}

View File

@@ -13,6 +13,7 @@ import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryStatus; import com.commafeed.backend.model.FeedEntryStatus;
import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.extended.FeedEntryWithStatus;
import com.commafeed.frontend.model.Entries; import com.commafeed.frontend.model.Entries;
import com.commafeed.frontend.model.Entry; import com.commafeed.frontend.model.Entry;
import com.google.common.base.Function; import com.google.common.base.Function;
@@ -85,21 +86,12 @@ public class EntriesREST extends AbstractREST {
int limit, boolean unreadOnly) { int limit, boolean unreadOnly) {
List<Entry> entries = Lists.newArrayList(); List<Entry> entries = Lists.newArrayList();
if (!unreadOnly) { List<FeedEntryWithStatus> unreadEntries = feedEntryService.getEntries(
List<FeedEntry> unreadEntries = feedEntryService.getEntries( subscription.getFeed(), getUser(), unreadOnly, offset, limit);
subscription.getFeed(), getUser(), true, offset, limit); for (FeedEntryWithStatus feedEntry : unreadEntries) {
for (FeedEntry feedEntry : unreadEntries) { entries.add(populateEntry(buildEntry(feedEntry), subscription));
entries.add(populateEntry(buildEntry(feedEntry), subscription,
true));
}
} }
List<FeedEntry> readEntries = feedEntryService.getEntries(
subscription.getFeed(), getUser(), false, offset, limit);
for (FeedEntry feedEntry : readEntries) {
entries.add(populateEntry(buildEntry(feedEntry), subscription,
false));
}
return entries; return entries;
} }
@@ -108,39 +100,35 @@ public class EntriesREST extends AbstractREST {
boolean unreadOnly) { boolean unreadOnly) {
List<Entry> entries = Lists.newArrayList(); List<Entry> entries = Lists.newArrayList();
if (!unreadOnly) { List<FeedEntryWithStatus> unreadEntries = feedEntryService.getEntries(
List<FeedEntry> unreadEntries = feedEntryService.getEntries( categories, getUser(), unreadOnly, offset, limit);
categories, getUser(), true, offset, limit); for (FeedEntryWithStatus feedEntry : unreadEntries) {
for (FeedEntry feedEntry : unreadEntries) {
entries.add(populateEntry(buildEntry(feedEntry), entries.add(populateEntry(buildEntry(feedEntry),
subMapping.get(feedEntry.getFeed().getId()), true)); subMapping.get(feedEntry.getEntry().getFeed().getId())));
}
} }
List<FeedEntry> readEntries = feedEntryService.getEntries(categories,
getUser(), false, offset, limit);
for (FeedEntry feedEntry : readEntries) {
entries.add(populateEntry(buildEntry(feedEntry),
subMapping.get(feedEntry.getFeed().getId()), false));
}
return entries; return entries;
} }
private Entry buildEntry(FeedEntry feedEntry) { private Entry buildEntry(FeedEntryWithStatus feedEntryWithStatus) {
Entry entry = new Entry(); Entry entry = new Entry();
FeedEntry feedEntry = feedEntryWithStatus.getEntry();
entry.setId(String.valueOf(feedEntry.getId())); entry.setId(String.valueOf(feedEntry.getId()));
entry.setTitle(feedEntry.getTitle()); entry.setTitle(feedEntry.getTitle());
entry.setContent(feedEntry.getContent()); entry.setContent(feedEntry.getContent());
entry.setDate(feedEntry.getUpdated()); entry.setDate(feedEntry.getUpdated());
entry.setUrl(feedEntry.getUrl()); entry.setUrl(feedEntry.getUrl());
FeedEntryStatus status = feedEntryWithStatus.getStatus();
entry.setRead(status == null ? false : status.isRead());
return entry; return entry;
} }
private Entry populateEntry(Entry entry, FeedSubscription sub, boolean read) { private Entry populateEntry(Entry entry, FeedSubscription sub) {
entry.setFeedName(sub.getTitle()); entry.setFeedName(sub.getTitle());
entry.setFeedId(String.valueOf(sub.getId())); entry.setFeedId(String.valueOf(sub.getId()));
entry.setRead(read);
return entry; return entry;
} }
@@ -156,16 +144,14 @@ public class EntriesREST extends AbstractREST {
FeedEntry entry = feedEntryService.findById(Long.valueOf(id)); FeedEntry entry = feedEntryService.findById(Long.valueOf(id));
markEntry(entry, read); markEntry(entry, read);
} else if (type == Type.feed) { } else if (type == Type.feed) {
List<FeedEntry> entries = Lists.newArrayList();
Feed feed = feedSubscriptionService.findById(Long.valueOf(id)) Feed feed = feedSubscriptionService.findById(Long.valueOf(id))
.getFeed(); .getFeed();
if (read) { if (read) {
entries.addAll(feedEntryService.getEntries(feed, getUser(), List<FeedEntryWithStatus> entries = feedEntryService
false)); .getEntries(feed, getUser(), false);
for (FeedEntryWithStatus entry : entries) {
markEntry(entry.getEntry(), true);
} }
entries.addAll(feedEntryService.getEntries(feed, getUser(), true));
for (FeedEntry entry : entries) {
markEntry(entry, read);
} }
} }
} }

View File

@@ -118,7 +118,7 @@ public class SubscriptionsREST extends AbstractREST {
sub.setName(subscription.getTitle()); sub.setName(subscription.getTitle());
sub.setMessage(subscription.getFeed().getMessage()); sub.setMessage(subscription.getFeed().getMessage());
int size = feedEntryService.getEntries(subscription.getFeed(), int size = feedEntryService.getEntries(subscription.getFeed(),
getUser(), false).size(); getUser(), true).size();
sub.setUnread(size); sub.setUnread(size);
category.getFeeds().add(sub); category.getFeeds().add(sub);
} }

View File

@@ -9,16 +9,16 @@
<query>select e from FeedEntry e where e.guid in (:guids) order by e.updated desc</query> <query>select e from FeedEntry e where e.guid in (:guids) order by e.updated desc</query>
</named-query> </named-query>
<named-query name="Entry.unreadByFeed"> <named-query name="Entry.unreadByFeed">
<query>select e from FeedEntry e where e.feed=:feed and not exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) order by e.updated desc</query> <query>select e, s from FeedEntry e LEFT JOIN e.statuses s WITH (s.user.id=:userId) where e.feed=:feed and not exists (select s2 from FeedEntryStatus s2 where s2.entry=e and s2.user.id=:userId and s2.read=true) order by e.updated desc</query>
</named-query> </named-query>
<named-query name="Entry.readByFeed"> <named-query name="Entry.allByFeed">
<query>select e from FeedEntry e where e.feed=:feed and exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) order by e.updated desc</query> <query>select e, s from FeedEntry e LEFT JOIN e.statuses s WITH (s.user.id=:userId) where e.feed=:feed order by e.updated desc</query>
</named-query> </named-query>
<named-query name="Entry.unreadByCategories"> <named-query name="Entry.unreadByCategories">
<query>select e from FeedEntry e where exists (select s from FeedSubscription s where s.user=:user and s.feed = e.feed and s.category in (:categories) ) and not exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) order by e.updated desc</query> <query>select e, s from FeedEntry e LEFT JOIN e.statuses s WITH (s.user.id=:userId) where exists (select s2 from FeedSubscription s2 where s2.user=:user and s2.feed = e.feed and s2.category in (:categories) ) and not exists (select s3 from FeedEntryStatus s3 where s3.entry = e and s3.user =:user and s3.read=true) order by e.updated desc</query>
</named-query> </named-query>
<named-query name="Entry.readByCategories"> <named-query name="Entry.allByCategories">
<query>select e from FeedEntry e where exists (select s from FeedSubscription s where s.user=:user and s.feed = e.feed and s.category in (:categories) ) and exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) order by e.updated desc</query> <query>select e, s from FeedEntry e LEFT JOIN e.statuses s WITH (s.user.id=:userId) where exists (select s2 from FeedSubscription s2 where s2.user=:user and s2.feed = e.feed and s2.category in (:categories) ) order by e.updated desc</query>
</named-query> </named-query>
</entity-mappings> </entity-mappings>

View File

@@ -5,6 +5,7 @@
http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="primary"> <persistence-unit name="primary">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>${jpa.datasource.name}</jta-data-source> <jta-data-source>${jpa.datasource.name}</jta-data-source>
<properties> <properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/> <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>

View File

@@ -87,6 +87,8 @@ module.factory('EntryService', [
module.service('SettingsService', function($resource) { module.service('SettingsService', function($resource) {
var s = {} var s = {}
s.settings = {};
s.settings.readMode = 'unread';
s.settings = $resource('rest/settings/get').get(); s.settings = $resource('rest/settings/get').get();
s.save = function() { s.save = function() {
$resource('rest/settings/save').save(s.settings); $resource('rest/settings/save').save(s.settings);