Query language in Play Framework 2 - java

How do I select from a table by another unique column other than id
im looking a replacement for
String fbid=<some facebookId>
User user = User.findBy("facebookId",fbid).first();
I'm using Java + Ebean and not scala!
thanks

User user = User.find.where().eq("facebookId", fbid).findUnique();
Of course you can create a 'shortcut' by adding a method into your User model:
public static User findByFacebookId(String fbid){
return find.where().eq("facebookId", fbid).findUnique();
}
and using it in controller:
User user = User.findByFacebookId(fbid);
It's pretty good solution as you can keep your controllers' code clean and use custom finders as sophisticated as required at the case.

Related

Find by where NOT current row

I am developing a small cms and I am using spring data jpa to do my database stuff.
When I add a new page, I want to make sure the slug doesn't already exist, for that purpose I added a method to my repository:
public interface PageRepository extends JpaRepository<Page, Integer> {
Page findBySlug(String slug);
}
That works fine when adding.
However when editing a page, I want to check that the slug doesn't already exist but NOT for the current page, how can I do that? I guess I could somehow pass the current row id or something like that, but how would I do that?
You can write
Page findBySlugAndIdNot(String slug,Long id)
where id is name of your identifier in entity with proper type. Look at documentation
you may try custom query like :
#Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM Page p WHERE p.slug = :slug and p.pageId!=pageId")
public boolean existsBySlugInPage(#Param("slug") String slug, #Param("pageId") Integer pageId);

Ignore an Ebean Version in certain situations

I'm using a Version field with Ebean, in Play Framework 2.2, but in certain situations I would actually rather have the version of an object not be updated. Is this at all possible?
So someone has an account on my website and they're looking at a post of another user. If the user updates that post, it's not automatically reloaded in the frontend. Please don't suggest I do this to solve the problem, I can't do it that way.
The problem is when a user gives the post a rating, the PUT call is refused if the user updated the post recently.
Is there a way to force Ebean to ignore the version field in specific situations like this?
Please don't suggest I do this to solve the problem, I can't do it that way.
LOL, nobody's gonna to suggest it to you :)
Custom statement should avoid updating the version:
SqlUpdate update = Ebean.createSqlUpdate("UPDATE post set likes = likes+1 where id = :id");
update.setParameter("id", post.id).execute();
(tested it, works as required)
As an alternative approach to using SqlUpdate in Ebean 4.x you can use 'stateless updates'. Note that for the counter = counter + 1 use case SqlUpdate is still a better fit.
Customer customer = new Customer();
customer.setId(42);
customer.setName("Please update the name")
// customer.setVersion(2) ... not setting the version property
// perform stateless update (and the version property was not set)
Ebean.update(customer);
// effectively results in:
update customer set name = 'Please update the name' where id = 42

I can't use any ID in my code

I was always taught to use IDs in my code to refer to records into the database.
But let's take the case we have same roles in the table Role. Now I want to query only the records related to the role Player:
ID ROLE
1 Admin
2 Organizer
3 Player
I don't know in my code the ID of Player, but I want to retrieve all the players, so with Hibernate I wrote:
String queryString = "from User u where u.role.role = ?";
Query queryObject = getSession().createQuery(queryString);
queryObject.setParameter(0, "player");
return queryObject.list();
As you can see I wrote "player" in the code. I think this is not the best way and I should use an ID instead. But I don't know the ID and it may change depending on the server on which I run the application. A second problem with my solution is that "player" can be capitalized into the database and this may be changed over time.
So, what should be the solution to all these problems? Is there any way to use the ID instead? Or any other way to improve this code?
In this case it seems that role should be an enum and your query would look something like:
queryObject.setParameter(0, Role.PLAYER);
Also, you might take a look at the criteria API which will help you create more type-safe queries that are more robust vs. refactoring.
You should create a enum class like this.
public enum Role {
Admin(1),
Organizer(2),
Player(3);
}
And change your code to
String queryString = "from User u where u.id= ?";
Query queryObject = getSession().createQuery(queryString);
queryObject .setParameter(0, Role.PLAYER);
return queryObject.list();
You can do using create a mapping table like ,
UserRoleMapping
ID - Incremental,
UserId - Refers to user table,
RoleId - Refers to role table
As one user can have more than one role so it will satisfy that thing too.
to Get the roles using query
select role.id from userrolemapping urm innerjoin on user u.id = urm.id where u.id = ?
using IDs or string/vachar etc. lookup is all dependent on the data that you have in the database. Some organization keep the id static and some keep the name/description static. So, make sure you have good understanding of your DB data and what will stay static . However, if the role is static you can use HQL ignore case like the example I provided for you below (I'm not adding information about the ID static path because others have already provided information about it for and don't want to duplicate it ).
--note you can take the percentages out if you only want "player"
String queryString = "from User u where lower( u.role.role ) like lower('%"+ ? +"%')";
Query queryObject = getSession().createQuery(queryString);
queryObject.setParameter(0, "player");
return queryObject.list();

Google App Engine HRD query without ancestor

I have a GAE project written in Java and I have some thoughts about the HRD and a problem that I'm not sure how to solve.
Basically I have users in my system. A user consists of a userid, a username, an email and a password. Each time I create a new user, I want to check that there isn't already a user with the same userid (should never happen), username or email.
The userid is the key, so I think that doing a get with this will be consistent. However, when I do a query (and use a filter) to find possible users with the same username or email, I can't be sure that the results are consistent. So if someone has created a user with the same username or email a couple of seconds ago, I might not find it with my query. I understand that ancestors are used to work around this problem, but what if I don't have an ancestor to use for the query? The user does not have a parent.
I'd be happy to hear your thoughts on this, and what is considered to be best practice in situations like these. I'm using Objectify for GAE if that changes anything.
I wouldn't recommend using email or any other natural key for your User entity. Users change their email addresses and you don't want to end up rewriting all the foreign key references in your database whenever someone changes their email.
Here's a short blurb on how I solve this issue:
https://groups.google.com/d/msg/google-appengine/NdUAY0crVjg/3fJX3Gn3cOYJ
Create a separate EmailLookup entity whose #Id is the normalized form of an email address (I just lowercase everything - technically incorrect but saves a lot of pain when users accidentally capitalize Joe#example.com). My EmailLookup looks like this:
#Entity(name="Email")
public class EmailLookup {
/** Use this method to normalize email addresses for lookup */
public static String normalize(String email) {
return email.toLowerCase();
}
#Id String email;
#Index long personId;
public EmailLookup(String email, long personId) {
this.email = normalize(email);
this.personId = personId;
}
}
There is also a (not-normalized) email field in my User entity, which I use when sending outbound emails (preserve case just in case it matters for someone). When someone creates an account with a particular email, I load/create the EmailLookup and the User entities by key in a XG transaction. This guarantees that any individual email address will be unique.
The same strategy applies for any other kind of unique value; facebook id, username, etc.
A way around the HRD's eventual consistency, is to use get instead of query. To be able to do this is you need to generate natural IDs, e.g. generate IDs that consists of data you receive in request: email and username.
Since get in HRD has strong consistency, you will be able to reliably check if user already exists.
For example a readable natural ID would be:
String naturalUserId = userEmail + "-" + userName;
Note: in practice emails are unique. So this is a good natural ID on it's own. No need to add a made-up username to it.
You may also enable cross-group transactions (see https://developers.google.com/appengine/docs/java/datastore/overview#Cross_Group_Transactions) and then in one transaction look for the user and create a new one, if that helps.
Recommend avoiding an indexed field and query unless you have other uses for it. Here is what I have done before (Python) using key_name (since entity ids need to be ints). Easy to use either the key_name or id for other entities that need to link to user:
username = self.request.get('username')
usernameLower = username.lower()
rec = user.get_by_key_name(usernameLower)
if rec is None:
U = user(
key_name = usernameLower,
username = username,
etc...)
U.put()
else:
self.response.out.write(yourMessageHere)

App engine datastore - query on Enum fields

I am using GAE(Java) with JDO for persistence.
I have an entity with a Enum field which is marked as #Persistent and gets saved correctly into the datastore (As observed from the Datastore viewer in Development Console). But when I query these entities putting a filter based on the Enum value, it is always returning me all the entities whatever value I specify for the enum field.
I know GAE java supports enums being persisted just like basic datatypes. But does it also allow retrieving/querying based on them? Google search could not point me to any such example code.
Details:
I have printed the Query just before being executed. So in two cases the query looks like -
SELECT FROM com.xxx.yyy.User WHERE role == super ORDER BY key desc RANGE 0,50
SELECT FROM com.xxx.yyy.User WHERE role == admin ORDER BY key desc RANGE 0,50
Both above queries return me all the User entities from datastore in spite of datastore viewer showing some Users are of type 'admin' and some are of type 'super'.
For time being, I have replaced the Enums with simple integer constants. Reported this case as an issue in the google app engine : http://code.google.com/p/googleappengine/issues/detail?id=2927
For a parameter other than a String or an int, I believe you need to use declareParameters instead. Try something like this:
Query q = pm.newQuery(com.xxx.yyy.User.class);
q.setFilter("role == p1"); //p1 is a variable place holder
q.declareParameters("Enum p1"); //here you define the data type for the variable, in this case an Enum
q.setRange(0, 50);
q.setOrdering("key desc");
AbstractQueryResult results = (AbstractQueryResult) pm.newQuery(q).execute(admin);
or if you want more gql like syntax -
Query query = pm.newQuery("SELECT FROM com.xxx.yyy.User WHERE role == p1 ORDER BY key desc RANGE 0,50");
query.declareParameters("Enum p1");
AbstractQueryResult results = (AbstractQueryResult) pm.newQuery(q).execute(admin);
You need to use your enum's class name when you declare the query parameter.
For example, if you build your query using the method style, and assuming your enum is called Role and is declared under the User class, you can do something like the following:
Query query = pm.newQuery(com.xxx.yyy.User.class);
query.setFilter("role == roleParam");
query.declareParameters(com.xxx.yyy.User.Role.class.getName() + " roleParam");

Categories