Let's say I have following classes. (only most important things included)
public class Client {
/* Some Properties */
}
public class ClientDocumentAssociation {
#ManyToOne
private Client client;
/* Some Properties */
}
#Indexed
public class Document {
#OneToOne
private ClientDocumentAssociation clientAssociation;
#Field(name = "text")
private String text;
/* Some Properties */
}
My basic document search is like this:
public List<AbstractDocument> searchDocuments(String text) {
if (text == null) {
return newArrayList();
}
FullTextEntityManager ftem = Search.getFullTextEntityManager(entityManagerProvider.get());
MultiFieldQueryParser parser = new MultiFieldQueryParser(DOCUMENT_FIELDS, new StandardAnalyzer());
parser.setDefaultOperator(Operator.AND);
FullTextQuery ftq;
try {
Query q = parser.parse(text + "*");
ftq = ftem.createFullTextQuery(q, Document.class);
ftq.setMaxResults(20);
List<AbstractDocument> results = ftq.getResultList();
return results;
} catch (ParseException e) {
e.printStackTrace();
}
return newArrayList();
}
Now, I want to be able to search for documents, but not in the scope of the whole index, but just find documents that belong to given Client. The only thing that comes to my mind is adding the association to the index and add client id to the appropriate field in search. But that does not seem right. There must be another option and that's what I am asking for.
Why does your initial idea seem to be wrong? In fact indexing all the data needed for your search is the recommended way of doing this. That's what #IndexedEmbedded is there for. Indexing the data will also give you more flexibility for changes in the query and/or new queries.
The other way you can do this is to use Filters. A filter can be applied to a Lucene search. Hibernate supports adding filters as annotations and enabling them at run time
Ok I actually found a solution. The thing I (and anyone who was searching for solution of the same problem) needed is setting up Criteria for the FullTextQuery.
Session session = (Session) ftem.getDelegate();
Criteria criteria = session.createCriteria(Document.class).createCriteria("clientAssociation").add(
Restrictions.eq("client", owner));
/* .... */
ftq.setCriteriaQuery(criteria);
Seems to work ok :)
Related
OK, so I have an interesting problem. I am using java/maven/spring-boot/cassandra... and I am trying to create a dynamic instantiation of the Mapper setup they use.
I.E.
//Users.java
import com.datastax.driver.mapping.annotations.Table;
#Table(keyspace="mykeyspace", name="users")
public class Users {
#PartitionKey
public UUID id;
//...
}
Now, in order to use this I would have to explicitly say ...
Users user = (DB).mapper(Users.class);
obviously replacing (DB) with my db class.
Which is a great model, but I am running into the problem of code repetition. My Cassandra database has 2 keyspaces, both keyspaces have the exact same tables with the exact same columns in the tables, (this is not my choice, this is an absolute must have according to my company). So when I need to access one or the other based on a form submission it becomes a mess of duplicated code, example:
//myWebController.java
import ...;
#RestController
public class MyRestController {
#RequestMapping(value="/orders", method=RequestMethod.POST)
public string getOrders(...) {
if(Objects.equals(client, "first_client_name") {
//do all the things to get first keyspace objects like....
FirstClientUsers users = (db).Mapper(FirstClientUsers.class);
//...
} else if(Objects.equals(client, "second_client_name") {
SecondClientUsers users = (db).Mapper(SecondClientUsers.class);
//....
}
return "";
}
I have been trying to use methods like...
Class cls = Class.forName(STRING_INPUT_VARIABLE_HERE);
and that works ok for base classes but when trying to use the Accessor stuff it no longer works because Accessors have to be interfaces, so when you do Class cls, it is no longer an interface.
I am trying to find any other solution on how to dynamically have this work and not have to have duplicate code for every possible client. Each client will have it's own namespace in Cassandra, with the exact same tables as all other ones.
I cannot change the database model, this is a must according to the company.
With PHP this is extremely simple since it doesn't care about typecasting as much, I can easily do...
function getData($name) {
$className = $name . 'Accessor';
$class = new $className();
}
and poof I have a dynamic class, but the problem I am running into is the Type specification where I have to explicitly say...
FirstClientUsers users = new FirstClientUsers();
//or even
FirstClientUsers users = Class.forName("FirstClientUsers");
I hope this is making sense, I can't imagine that I am the first person to have this problem, but I can't find any solutions online. So I am really hoping that someone knows how I can get this accomplished without duplicating the exact same logic for every single keyspace we have. It makes the code not maintainable and unnecessarily long.
Thank you in advance for any help you can offer.
Do not specify the keyspace in your model classes, and instead, use the so-called "session per keyspace" pattern.
Your model class would look like this (note that the keyspace is left undefined):
#Table(name = "users")
public class Users {
#PartitionKey
public UUID id;
//...
}
Your initialization code would have something like this:
Map<String, Mapper<Users>> mappers = new ConcurrentHashMap<String, Mapper<Users>>();
Cluster cluster = ...;
Session firstClientSession = cluster.connect("keyspace_first_client");
Session secondClientSession = cluster.connect("keyspace_second_client");
MappingManager firstClientManager = new MappingManager(firstClientSession);
MappingManager secondClientManager = new MappingManager(secondClientSession);
mappers.put("first_client", firstClientManager.mapper(Users.class));
mappers.put("second_client", secondClientManager.mapper(Users.class));
// etc. for all clients
You would then store the mappers object and make it available through dependency injection to other components in your application.
Finally, your REST service would look like this:
import ...
#RestController
public class MyRestController {
#javax.inject.Inject
private Map<String, Mapper<Users>> mappers;
#RequestMapping(value = "/orders", method = RequestMethod.POST)
public string getOrders(...) {
Mapper<Users> usersMapper = getUsersMapperForClient(client);
// process the request with the right client's mapper
}
private Mapper<Users> getUsersMapperForClient(String client) {
if (mappers.containsKey(client))
return mappers.get(client);
throw new RuntimeException("Unknown client: " + client);
}
}
Note how the mappers object is injected.
Small nit: I would name your class User in the singular instead of Users (in the plural).
I have an issue i'm struggling with for some time now. Im trying to implement a news feed feature in my app using GAE cloud endpoints and java. The common concept is of followers and followees, where an action of a followee can be seen by his followers. A new follower should also see his followees past actions, not only from the time he started following.
I made a few tries with the following components. Each try worked great but was lacking something:
On each user action i added a 'log' entity into the datastore with the user id included. When a user was displaying his news feed i just queried for all those entities by their user ids according to the user's followees list. Everything was fine until i realized that a 'IN' query cannot be cursored. So this option was gone.
On this try, which is also the current state of the application, im using the Search API. Upon every user action im not storing a 'log' entity into the datastore anymore but a document into a search index. Complex queries can be cursored here and the world is smiling again. But... im not too sure that, billing wise, this is a smart descision. It seems that the costs of searching/adding/deleting documents along side the documented daily limitations is making the whole thing a bit too sketchy.
The next try should be Prospective Search API. From what i'm reading in the documents it seems the right component to pick for that purpose. Unfortunately, the documentation is really poor and give very little examples. Also the billing information is unclear.
So im asking for the advice of the stackoverflow community. Can you please advise me about this matter ? and if Prospective Search is the right option to choose, can you please provide some clear sample java code that uses cloud endpoints?
EDIT : Just to emphasize the main design requirement here - The news feed feature need to have the ability to fetch sorted followees actions using a cursor (in order avoid querying the whole batch).
Use a pull-aggregate-per-follower model: periodically (or on demand) query all followees actions once and then cache them inside a dedicated per-follower entity. Remember the time of last query, so next time you just query from that point on (assuming actions can not be added/changed to the past times).
This will give you the following features (and limitations):
If query is on-demand, than you will not need to query for users that are inactive.
Since the query is "new-only" (looks for new actions only), it would cost you nothing if it returned zero results.
You will only query each followee actions per follower once. After that all recent actions would be cached inside one entity and loaded into memory with one get. This should be a substantial cost and time saving.
You could sort/filter actions in memory any way you wish.
Limitations:
Entities have a 1MB limit, so there is a max no of actions that you can cache in one entity. So you will either need to limit caching of recent actions per user or spread out action caching over multiple entities.
You will need to use IN query over followees (max 30) and also use parallel threads to achieve decent performance. This could easily hit 3-5 seconds when querying over 1000-2000 followees. Also, you could easily hit RPC limit (aka max concurrent API calls) per instance when serving multiple users at the same time.
I hope I understand the question correctly - you want to implement a news feed into your application and allow users to follow each other. The new followers need to be able to see the users actions. I am sure there are multiple other ways of solving this problem, but I will attempt to help you out by providing a solution that makes use of JAVA JDO to access the datastore.
I would first design the entity relationships in JDO as follows:
1 User to many actions.
1 User to many followers (User).
1 User to many following (User).
Here are simple JDO classes:
User Class:
#PersistenceCapable(identityType=IdentityType.APPLICATION)
public class User {
#PrimaryKey
#Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String userId; // Google unique user ID, could also store user email.
#Persistent
private Set<Key> actions;
#Persistent
private Set<Key> followers;
#Persistent
private List<Key> following;
public User(Key key, String userId) {
this.key = key;
this.userId = userId;
this.actions = new HashSet<Key>();
this.followers = new HashSet<Key>();
this.following = new HashSet<Key>();
}
public Key getKey() {
return this.key;
}
public void addAction(Key actionKey) {
this.actions.add(actionKey);
}
public void addActions(Set<Key> actionKeys) {
this.actions.addAll(actionKeys);
}
public Set<Key> getActions() {
return this.actions;
}
public void addFollower(Key followerKey) {
this.followers.add(followerKey);
}
public void addFollowers(Set<Key> followerKeys) {
this.followers.addAll(followerKeys);
}
public Set<Key> getFollowers() {
return this.followers;
}
public void addFollowing(Key followingKey) {
this.following.add(followingKey);
}
public void addAllFollowing(Set<Key> followingKeys) {
this.following.addAll(followingKeys);
}
public Set<Key> getFollowing() {
return this.following;
}
}
Action Class:
#PersistenceCapable(identityType=IdentityType.APPLICATION)
public class Action {
#PrimaryKey
#Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
Date date;
#Persistent
private String title;
public Action(Key key, String title) {
this.key = key;
this.title = title;
this.date = new Date(); // date of creation (now).
}
public Key getKey() {
return this.key;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return this.title;
}
}
The Action class makes use of a Date property, you can refer to the documentation for applicable data types in the datastore. When an action is created, a Date object is allocated and initialized so that it represents the time at which it was allocated, measured to the nearest millisecond.
In my example above I linked the entities by their Keys, you could instead link them by their classes as follows:
List<Action> actions;
The relationship in my example is one of an unowned one-to-many relationship, perhaps it should be owned one-to-many. More information here for your to take a look and perhaps decide which would be best for your solution.
Once the relationships have been defined, you can create your endpoint classes around the JDO model classes. This will create basic api methods. You might want to change the endpoint class methods to suit your needs, for example change the way an action is created. A basic example would be to create the key from the actions title as follows (ActionEnpoint.java):
...
#ApiMethod(name = "insertAction")
public Action insertAction( #Named("title") String title ) {
PersistenceManager pm = getPersistenceManager();
Key key = KeyFactory.createKey(Action.class.getSimpleName(), title);
Action action = null;
try {
action = new Action(key, title);
pm.makePersistent(action);
} finally {
pm.close();
}
return action;
}
...
If you want to, you can add a method to your UserEndpoint class to query the datastore and return all actions belonging to that user and per date using the datastore query objects.
You need to add a method to your UserEndpoint class that allows you to add an action to that user, here is a simple example:
...
#ApiMethod(name = "addActionToUser")
public Achiever addActionToUser(
#Named("userId") String userId,
#Named("actionTitle") String actionTitle) {
PersistenceManager pm = getPersistenceManager();
Key userKey = KeyFactory.createKey(User.class.getSimpleName(), userId);
Key actionKey = KeyFactory.createKey(Action.class.getSimpleName(), actionTitle);
User user = null;
try {
user = (User) pm.getObjectById(User.class, userKey);
user.addAction(actionKey);
pm.makePersistent(user);
} catch (Exception e) {
}
return user;
}
...
Once all of the above is complete you can easily get the list of actions per user by calling the getUser method in your UserEndpoint class, which returns a User object. You can then call [ReturnedUserObject].getActions(). A new follower can now view all of the "followees" actions by just calling the api method to get that "followees" object and get his/her actions. You can then just sort the actions by date or however you envision it.
I hope I understood your question correctly, I was unsure about the first component you mentioned, but it seemed as though you got your relationships mixed up. I hope this solution points you in the right direction at least :).
If you need any additional help or clarification, or my answer was completely off point to what you were looking for then please let me know.
Kind regards,
Miki
I am new to Java and I'm trying to implement a basic database access layer.
I'm using Apache DBUtils to reduce JDBC boilerplate code and this is working really well.
The problem is that my implementation uses a separate class for CRUD for each table in my database and it feels wrong to be duplicating so much functionality.
Is this an acceptable design and if not what can I do to reduce code duplication?
Could I refactor my solution to use generics in some fashion?
I realize I could use an ORM (myBatis, Hibernate etc) as a solution but I would like to try to stick with DBUtils and plain JDBC if I can help it.
Just for clarification:
Lets say I have 2 tables...
---------------------
User | File
---------------------
userId | fileId
name | path
age | size
---------------------
In my current solution I would create 2 classes (UserStore, FileStore) and
each class would implement similar basic CRUD methods:
protected boolean Create(User newUser)
{
QueryRunner run = new QueryRunner(dataSource);
try
{
run.update("INSERT INTO User (name, age) " +
"VALUES (?, ?)", newUser.getName(), newUser.getAge());
}
catch (SQLException ex)
{
Log.logException(ex);
return false;
}
return true;
}
protected User Read(int userId)
{
try
{
User user = run.query("SELECT * FROM User WHERE userId = ? ", userId);
return user;
}
catch (SQLException ex)
{
Log.logException(ex);
return null;
}
}
protected update(User user)
{
... perform database query etc
}
protected delete(int userId)
{
... perform database query etc
}
You asked how i would do this with Template method. Here is an example how you could do it:
public class AbstractDAO<T> {
private String table;
private String id_field;
public AbstractDAO(String table, String id_field){
this.table = table;
...
}
public T read(int id){
try
{
T user = run.query("SELECT * FROM "+ table + " WHERE "+id_field +" = ? ", id);
return user;
}
catch (SQLException ex)
{
Log.logException(ex);
return null;
}
}
This one looks easy, how about Create?
public boolean Create(T user){
QueryRunner run = new QueryRunner(dataSource);
try
{
run.update("INSERT INTO "+table+ getFields() +
"VALUES " + getParameters(user));
}
catch (SQLException ex)
{
Log.logException(ex);
return false;
}
return true;
}
protected abstract String getFields();
protected abstract String getParameters(T user);
Ugly, and insecure, but okay for transmitting the idea.
It looks like I can give you few simple suggestions for your question.
1)
Instead of managing queries inside DAOs like what you are doing, make a factory class that has list of queries for your needs.
like
class QueryFactory {
static String INSERT_BOOK = "BLAH";
static String DELETE_BOOK = "BLAH";
}
This will separate queries from DAO code and make it easier to manage.
2)
Implement a generic DAO
http://www.codeproject.com/Articles/251166/The-Generic-DAO-pattern-in-Java-with-Spring-3-and
3) As you have mentioned above, use ORM to help yourself binding beans to Database and many more features.
Using DBUtils you have already abstracted away a lot of boilerplate code. What remains is mostly the work that differs between entities : The right sql statement, transformation of entity objects into UPDATE parameters and vice versa with SELECTs, exception handling.
Unfortunately it is not easy to create a general abstraction that is flexible enough for these remaining tasks. That's what ORM mappers are all about. I would still suggest to look into one of these. If you stick to the JPA API, you are still in standards land and able to switch the ORM provider more easily (although there is always some coupling).
I was impressed by SpringData's Repository abstraction. In simple use cases they give you zero code DAO's. If you are already using Spring and just want to persist your object model you should definitely look into it.
Alternatively I made some good experiences with jooq. It can also create DTO's and corresponding DAO's based on the tables in your schema. In contrast to ORM mappers it is closer to the relational schema, which may be an advantage or a disadvantage.
mybatis allows you to return a resulttype as hashmap:
<select id="mCount" resultType="hashmap">
select managerName, count(reportees) AS count
from mgr_employee
group by managerName;
</select>
So you can effectively write out in a workflow like this:
1) Develop an interface
2a) Use mybatis annotation to define the query required OR
2b) Link the interface to a xml and write the query
Take note that this will not be involving any DAO and other boilerplates involve as above
Not sure if this is a decent question or not but here it goes. We are trying to implement a UI testing framework (selenium web-driver) and want to use a Page driven design for example
class HomePage {
#FindBy(how = How.Id, id="myPageHeaderID")
private String pageHeader
In the simple example above I need to hard-code the "myPageHeaderID" string literal. One of the requirements proposed is that we be able to pull in the "myPageHeaderID" from a property for both maintenance reasons (no code deploy if something changes) and for internationalization reasons. I have been searching around and probably not doing a proper search but is there any way of doing what I am asking above?
I briefly went down this route, but due to our application it wasn't quite achievable (pages aren't always displayed in the same order once you've visited a page).
public class PageElement implements WebElementAdapter, Locatable {
private How how;
private String using;
private boolean required;
#FindBy(how = How.ID_OR_NAME, using = DEFAULT_LOCATION_STRATEGY)
private WebElement backingElement;
public PageElement(How how, String using using) {
this.how = how;
this.using = using;
this.required = true;
}
/**
* This is how the overriding of the element location is done. I then injected
* these values in a spring configured bean file.
*
* This is needed on your config file:
* default-lazy-init="true" default-init-method="initialize">
*/
public final void initElement() {
if (backingElement == null || isStale() {
backingElement = getDriver().findElement(getLocationStrategy());
}
}
public By getLocationStrategy() {
By by = new ByIdOrName(using.replace(DEFAULT_LOCATION_STRATEGY, using));
switch(how) {
case CLASS_NAME:
by = By.className(using.replace(DEFAULT_LOCATION_STRATEGY, using));
break;
//Do for others
}
return by;
}
public WebElement getBackingElement() {
return backingElement;
}
}
public interface WebElementAdapter {
WebElement getBackingElement();
}
public interface Locatable {
By getLocationStrategy();
}
I then created common widgets in POJOs, and injected these into page objects which were a collection of these widgets.
From there I had a simple test harness which was responsible for taking in strings (which were then executed. Basically it allowed for test cases to be written in SpEL and act on the beans which were injected.
It was what I thought a pretty neat project, but I had to shelf it to get some other things done.
Annotations are essentially metadata. Taking database metadata for example, it would be weird if Oracle database would turn into MySQL, right? Here is the article about Annotation Transformers in TestNG. Didn't try it myself, but I think it could be implemented in some way or another.
AFAIK, you can call a method from the Annotation.
#FindBy(how = How.Id, id=getProp())
private String pageHeader;
private String getProp()
{
String prop = //whatever way you want to get the value
return prop;
}
Doesn't that work?
I love the type safety CriteriaQuery brings ing JPA 2.0 but it also brings a bit of boiler-plate code. For example, let say I have an entity called NamedEntity, which simply has an id and a String field called "name" (assume it has the unique constraint set to true). Here's what the NamedEntityManager might look like:
public class NamedEntityManager
{
//inject using your framework
EntityManager entityManager;
//retrieve all existing entities of type NamedEntity from DB
public Iterable<NamedEntity> queryAll()
{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class);
return entityManager.createQuery(query).getResultList();
}
//retrieve a single entity of type NamedEntity from DB using specified name
public NamedEntity queryByName(String name)
{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class);
Root<NamedEntity> root = query.from(NamedEntity.class);
query = query.where(root.<NamedEntity>get("name").in(name));
//skipped the try/catch block for the sake of brevity
return entityManager.createQuery(query).getSingleResult();
}
}
Is there a way to condense the code in order to avoid copying/pasting the same lines of code into each query method? Perhaps somehow reuse the CriteriaQuery object?
I was looking for something like that, you could take a look at Querydsl (LGPL licensed) which can have JPA as backend.
Im still reading into it, but from their examples, it looks pretty clean.
HQLQuery q = new HibernateQuery(session);
QCat cat = new QCat("cat"); // query type
List<Cat> cats = q.from(cat).where(cat.name.between("A", "B")).list(cat);
In JPA 2.1, it will most probably be possible to mix JPQL and Criterias. With such an approach you could define a base query with JPQL and then use the Criteria API to dynamically add small parts.
I figure the API will be less verbose then, since you only need to use small parts of it.
Then Use JPA-2.0 MetaData model.
http://docs.jboss.org/hibernate/jpamodelgen/1.0/reference/en-US/html_single/
It seems there's no way to reduce the amount of code. I guess something had to be sacrificed to gain type safety.
Way outdated, this post, but I want to add what I recently built for simple queries
public static class Jpa2Whatsoever {
private final EntityManager em;
public class Jpa2WhatsoeverProgress<T> {
private CriteriaQuery<T> cq;
private List<Predicate> predicates = new ArrayList<>();
private Root<T> root;
public Jpa2WhatsoeverProgress(Class<T> type) {
this.cq = em.getCriteriaBuilder().createQuery(type);
this.root = cq.from(type);
}
public Jpa2WhatsoeverProgress<T> where(String attributeName, Object value) {
Predicate equal = em.getCriteriaBuilder().equal(root.get(attributeName), value);
predicates.add(equal);
return this;
}
public List<T> getResultList() {
Predicate[] predicatesArray = new Predicate[predicates.size()];
TypedQuery<T> typedQuery = em.createQuery(cq.select(root).where(predicates.toArray(predicatesArray)));
List<T> resultList = typedQuery.getResultList();
return Collections.unmodifiableList(resultList);
}
}
public Jpa2Whatsoever(EntityManager entityManager) {
this.em = entityManager;
}
public <T> Jpa2WhatsoeverProgress<T> select(Class<T> type) {
return new Jpa2WhatsoeverProgress<T>(type);
}
}
You can use it like this
List<MyEntity> matchingEntities = new Jpa2Whatsoever(entityManager).select(MyEntity.class).where("id", id).where("due", new Date()).getResultList();
In the end I stopped this. Mainly because I saw that I had only two queries and I would have to extend the DSL to get the required query characteristics into it, such as
greater than, less than
Metamodel support
QueryBuilder.currentDate() and alike.
Further, I find it ugly to always call where while it actually corresponds to a more SQLly and. Anyway, if someone is interested in a very simple query API, it is still worth a try.
BTW: Forget about the names, this was a prototype, nothing more.