Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] EclipseLink performance

Hi Tom,

Thanks to you and Samba, the suggestions were really helpful. I am done.


Regards,
Jehanzeb Qayyum





On Tue, Jan 12, 2010 at 1:14 AM, Tom Ware <tom.ware@xxxxxxxxxx> wrote:
Hi Quayym,

 I have not had access to email for a couple of weeks.

 Have Samba's suggestions been helpful?  Are there questions in this thread you still need answers to?

-Tom

Samba wrote:
Hi Quayym,

you can create a */simplistic permission model/* like the following:

you can manage a generic table where you store the authorization information; some thing like the one shown below will work:


Table : *Authorization_Information*
*
*
*I*/*D      USER_ID       BO_ID       BO_TYPE     ALLOWED_ACTIONS*/


For BO_ID, you would save the id of the business object irrespective of whether the object is an audio or a video or another type; and you would fill the type column with the type of business object; i.e. audio, or video, etc. you can even store integer values in this column and map it in a static Java class what integer value is for which business object type.

Here there will no referential integrity between
You need to reserve two fixed values, Long.MAX_VALUE and Long.MIN_VALUE for BO_ID column to facilitate permission definitions, which I'll explain below

you can use the standard Java model for actions, like:

view=2^0=1
edit=2^1=2
add=2^2=4
delete=2^3=8

if  view and edit, then 1+2
if view edit and delete, then 1+2+8
if view add and edit, then 1+2+4

and so on....

when you are saving a business object, save also the authorization for that object as given below:

/AuthorizationInfo authzInfo= new AuthzInfo();/
/authzInfo.setBusinessObjectId(businessObject.getId());/
/authzInfo.setBusinessObjectType(businessObject.getType());/
/
/
/for a user who created this object:/
/authzInfo.setAllowedActions(AuthorizableActions.VIEW+AuthorizableActions.EDIT+AuthorizableActions.DELETE);/
/
/
/for a user who subscribed to this object:/
/authzInfo.setAllowedActions(AuthorizableActions.VIEW);/
/
/
/for  users who want to collaborate on a shared object:/
/authzInfo.setAllowedActions(AuthorizableActions.VIEW+AuthorizableActions.EDIT);/
/
/
/for 'add' permission, you need to use the reserved id values for BO_ID:/
/authzInfo.setId(Long.MAX_ID);/
/// when Long.MAX_VALUE is set for this column, then we can infer that the below mentioned actions are given over every object of the BO_TYPE specified in this record/
/// when Long.MIN_VALUE is set for this column, then we can infer that the below mentioned actions are given over only the objects of the BO_TYPE specified in this record that are explicitly created by the user himself /
/
/
/authzInfo.setBusinessObjectType(businessObject.getType());/
/authzInfo.setActions(AuthorizableActions.ADD);/
/
/
/you can even give any combination of actions for all the objects of a particular business object type./
/authzInfo.setAllowedActions(AuthorizableActions.VIEW+ AuthorizableActions.EDIT+ AuthorizableActions.ADD+ AuthorizableActions.DELETE);/


now, when you are checking whether a user has permission to view or edit or delete a certain business object, then you can make the authorization calls like:

/*Query 1: */

/String queryString="SELECT OBJECT(AI) FROM AuthorizationInfo AI WHERE USER_ID=:loggedInUserId";/
/Query query query=em.createQuery(queryString);/
/query.setParameter("loggedInUserId", loggedInUserId);/
/List<AuthorizationInfo> authorizationData=query.getResultList();/

and check if the business object that you are validating is in the return authorizationData ;  and if not throw security exception.

You can even make a join with AUTHORIZATION_INFORMATION table in all your queries that deal with business data objects to make sure that a logged in user is always operating on the authorized business objects, thus providing authorization checks in the database as well.
your queries for a business object audio may be like:

*/Query 2:/*
*/
/*
/String queryString="SELECT OBJECT(A) FROM Audio , AuthorizationInfo AI WHERE AI.userId=:loggedInUserId AND AI.businessObjectId=A.id AND AI.businessObjectType=:businessObjectType";/
/Query query=em.createQuery(queryString);/
/query.setParameter("loggedInUserId ", loggedInUserId );/
/query.setParameter("businessObjectType", Audio.getType());//type is a static discriminating variable for every class that extends the base BusinessObject class /
/List<Audio> authorizedAudioFilesForLoggedInUser=query.getResultList();/


*/Or/*,

 If you want to */reuse /*the permissions defined, then you can achieve that by modifying the above model a little to split the AUTHORIATION_INFORMATION table  in to three tables as shown below:


/Table/ : *Authorization_Information*
*
*
*I*/*D      BO_ID       BO_TYPE     ALLOWED_ACTIONS    ROLE_ID*/

*/
/*
*/
/*
*/Table :  ROLE/*
*/ID      NAME    DESCRIPTION   DEFAULT/*
*/
/*
*/
/*
*/
/Table/ : *USER_ROLE_ASSOCIATION*
*
*
/*USER_ID       ROLE_ID*/
*/
/*
*/
/*
*/
/*
*/note: you can treat a default role as one that is assigned to every user when the user is created, and that such role can not be deleted./*
*/
/*
/then you need to query these 3 tables for the authorization information./
/
/
/some thing like:/
/
/
/*Query 3:* /
/
/
/String queryString="SELECT AI.*,R.id,R.name,R.possedUsers.id <http://R.possedUsers.id>,R.possessedUsers.name <http://R.possessedUsers.name> FROM  Role R  JOIN AuthorizationInfo AI  WHERE R.possessedUsers.id <http://R.possessedUsers.id>=:loggedInUserId";/

Query query=em.createQuery(queryString);
/query.setParameter("loggedInUserId", loggedInUserId);/
/List<AuthorizationData> =query.getResultList();/
/
/
/where AuthorizationData is a utility object that is not a mapped  entity but can be used to represent a composition of data from a join of the three authorization tables. In fact you can even create a view based on the above query and just do  a select * for the logged in user on the view to get the authorization information for the user./
/
/
/
/
/If you want to do the authorization in your queries it self, then you can change your query described in  query 2 to :/
/
/
/*Query 4 :*/
/SELECT AI.*,R.id,R.name,R.possedUsers.id <http://R.possedUsers.id>,R.possessedUsers.name <http://R.possessedUsers.name> FROM  Role R JOIN   AuthorizationInfo AI  WHERE R.possessedUsers.id <http://R.possessedUsers.id>=:loggedInUserId/
/
String queryString="SELECT OBJECT(A) FROM Audio , AuthorizationInfo AI /JOIN /Role R WHERE R.possessedUsers.id <http://R.possessedUsers.id>=:loggedInUserId AND AI.businessObjectId=A.id AND AI.businessObjectType=:businessObjectType";

Query query=em.createQuery(queryString);
query.setParameter("loggedInUserId ", loggedInUserId );
query.setParameter("businessObjectType", Audio.getType());//type is a static discriminating variable for every class that extends the base BusinessObject class List<Audio> authorizedAudioFilesForLoggedInUser=query.getResultList();

/
/-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
/*


I hope this helps,

Regards,
Samba

On Tue, Dec 29, 2009 at 10:27 AM, jz <jehanzeb.qayyum@xxxxxxxxx <mailto:jehanzeb.qayyum@xxxxxxxxx>> wrote:

   Hi,

   Thanks for your answers i will try and let you know.
   Anything you have to say on instance based authorization using
   eclipselink, please share?

   Regards,
   Jehanzeb Qayyum






   On Tue, Dec 29, 2009 at 11:09 PM, Samba <saasira@xxxxxxxxx
   <mailto:saasira@xxxxxxxxx>> wrote:

       Hi Qayyum,
         I can offer the below explanations for two of your questions:
               1. for the delete use case, you can write a JPQL query to delete
       the record, example:

       String queryString= "DELETE OBJECT(BO) FROM  " +
       businessObject.getClass().getCanonicalName()+ " BO " +  "WHERE
       BO.id=:" + businessObject.getId();
       Query query=em.createQuery(queryString);
       int result= em.executeUpate(query);
       return result==1?true:false;

       however,there is a small downside to this; we don't get an
       OptimisticLockException when we try to delete a record that has
       been changed just before we executed this delete query. So we
       may never see certain updates to the deleted objects; if it
       crucial for your use case to always make sure that the object
       being deleted is in a certain state then, you need to write
       additional filter conditions n the where clause like :
       BO.version=businessObject.getVersion, and depending on the
       returned value, if zero, we have to assume that the deletion
       failed because the record has been updated by some other
       parallel transaction and that the version number might have
       changed; and hence rollback the current activity and tell the
       user to start the transaction afresh. ofcourse, this is all
       required if you want to make sure that you always delete the
       latest version of the object.
                       2. Yes, I too observed that eclipselink is generating an
       uncessary join when all I want is a reference to the id that my
       table-in-question already has as a foriegn key reference.        you can view the mail thread I posted earlier
       at: http://www.nabble.com/unnecessary-join-made-in-case-of-Many%3C--%3EMany-relation-tp24679954p24703970.html

       I did not raise a bug for this as suggested by James since I
       myself am not convinced at that time that this is really an
       issue with eclipselink and attributed to my data model design
       where we have too many eagerly loaded objects.

       Looking at your issue, I'm thinking to revisit this  and verify
       that the issue is infact produced by eclipselink, and if yes, I
       will raise a bugzilla request.

       Regards,
       Samba



       On Fri, Dec 25, 2009 at 1:47 PM, jz <jehanzeb.qayyum@xxxxxxxxx
       <mailto:jehanzeb.qayyum@xxxxxxxxx>> wrote:

           Hi Tom,

           Yes you are right i can always run a native query to fine
           tune, got your persistence context cache point but i am
           using ejb method to do jpa work and my entity manager is
           transaction demarcated not extended.            But since i am implementing a generic wrapper which should
           work for all entities i do not want to do that. Currently in
           my ejb i have an operation delete(BusinessObject bo); Now
           what should be the best implementation of this ejb method
           with following goals in mind:


           1- Performance
           2- Cascade delete relationships
           3- Delete operation should require minimal possible data
           best case only primary key of the record to remove
           4- Validate if entity being removed does exist in db

           //Note: All my jpa entities inherit from BusinessObject
           interface
           interface BusinessObject{
            public Object getId();
           }

           I have following alternatives:
           1- em.remove(em.merge(bo));
           On transaction commit this is will first update in db and
           then delete it. This breaks goal 1 and 3 because if i send a
           BusinessObject instance with only id set it will generate
           all sorts of errors on updating the record with nulls in
           other properties / columns

           2-            BusinessObject storedBo = em.read(bo.getId());
           if(storedBo != null){
            em.remove();
           }
           This defeats goal 1 of performance. But is still better than
           first case as it achieves goal 3.

           I do not know how to achieve goal 2 using JPA apart from
           manually specifying ON CASCADE DELTE on fk relationships. I
           wonder why EclipseLink does not generate cascade constraints
           in schema script.


           Secondly the question about manytoone relationship joins was
           a separate question. Most of my jpa entities have a
           manytoone relationship with a User entity. Whenever i do any
           operation on any entity i want to restrict that logged in
           user can perform operation on its own data (instance based
           authorization). For example one such operation is querying
           for data. User sends a query            SELECT o FROM Audio o
           i will modify this query to make it            SELECT o FROM Audio o WHERE o.user.id <http://o.user.id> =

           ?logged_in_userid

           The above jpa query translates to a join with User table,
           even when Audio table has a foreign key user id. This is a
           performance hit and i want to avoid it. To add a such a
           condition to jpa string query is also an interesting part.
           You may want to hear it and give comments. I researched on
           it and found out that JPA 1.0 do not have support for
           manipulating queries dynamically, it is however provided in
           JPA 2.0 with Criteria API. So i have to write a little
           JpqlWrapper class. I searched EclipseLink sourced code to
           find a jpql parser that can make my code more robust and
           error free but failed. I know below case miss a lot from
           jpql BNF but it worked for my scenario. Any suggestion on
           this one are highly appreciated.

           public class JpqlWrapper {
           Log log = LogFactory.getLog(this.getClass());
           StringBuilder query;

           public JpqlWrapper(String query) {
           this.query = new StringBuilder(query);
           }

           public void and(String condition) {
           String lowercase = query.toString().toLowerCase();
           if (!lowercase.isEmpty()) {
           int appendAt = lowercase.lastIndexOf(" order by");
           if (appendAt == -1) {
           appendAt = lowercase.length();
           }
           if (lowercase.indexOf(" where ") == -1) {
           query.insert(appendAt, " where " + condition + " ");
           } else {
           query.insert(appendAt, " and " + condition + " ");
           }
           }
           }

           public String getFirstIdentificationVariable() {
           Pattern pattern = Pattern.compile("from +\\w+ +\\w+",
           Pattern.CASE_INSENSITIVE);
           Matcher matcher = pattern.matcher(query);
           if (matcher.find()) {
           return matcher.group().substring(
           matcher.group().lastIndexOf(" ") + 1);
           }
           throw new IllegalArgumentException("Unexpected query: "
           + query.toString());
           }

           public String getFromObject() {
           Pattern pattern = Pattern
           .compile("from +\\w+", Pattern.CASE_INSENSITIVE);
           Matcher matcher = pattern.matcher(query);
           if (matcher.find()) {
           log.debug("Match: " + matcher.group());
           return
           matcher.group().substring(matcher.group().lastIndexOf(" ") + 1);
           }
           throw new IllegalArgumentException("Unexpected query: "
           + query.toString());
           }

           public String toString() {
           return query.toString();
           }

           }
           In my query ejb method i do something like.

           if (entity instanceof UserAssociation) {
           UserAssociation userAssociated = (UserAssociation) entity;
           userAssociated.setAssociatedUser(getCallingUser());
           jpql.and(jpql.getFirstIdentificationVariable() + "."+
           userAssociated.getUserAssociation() + ".id = "+
           userAssociated.getAssociatedUser().getId());
           }

           em.query(jpql.toString())


           Note in my ejb i get the executing user id from jaas
           principals collections using weblogic specific code (bad i
           know but isCallerInRole and getCallerPrincipal were not
           working):                      Set<Principal> principals =
           weblogic.security.Security.getCurrentSubject().getPrincipals();
           So moving on i had to do the same instance based
           authorization in case of create, update, delete and read
           operations.            For create i create a new User instance and set it on the
           entity.
           For update i have to read the entity, verify that if belongs
           to executing user and then set it on the instance,
           For delete i again have to read the entity, verify if it
           belongs to executing user
           For read i have to query instead of em.find() as i have to
           insert and condition for user.

           To enable all of above, my entities inherit an interface            interface UserAssociation{
             User getAssociatedUser();
             void setAssociatedUser();              String getUserAssociation(); //return user association in
           form of string that i can concatenate                                           //in jpql e.g."user",
           "group.user" etc
           }

           If you have a better way to do this instance based
           authorization, please share.


           Thanks for your time.

           Regards,
           Jehanzeb Qayyum






           On Thu, Dec 24, 2009 at 7:09 PM, Tom Ware
           <tom.ware@xxxxxxxxxx <mailto:tom.ware@xxxxxxxxxx>> wrote:

               Hi jz,

                You should be able to use a native query to run any
               pure SQL you want to run.  If you are deleting with a
               native query, you will want to be careful that you
               definitely have not read the object yet since a native
               query will not remove objects from the persistence
               context or the cache.

                Can you give an example of the query you are running
               and the SQL that is produced that causes your issue with
               m-1 relationships?

               -Tom

               jz wrote:

                   Hi,

                   How can i delete a detached entity without reading
                   it first?

                   Why eclipselink creates a join sql in case of
                   manytoone relationships if query contains only FK?
                                       Regards,
                   Jehanzeb Qayyum




                   ------------------------------------------------------------------------

                   _______________________________________________
                   eclipselink-users mailing list
                   eclipselink-users@xxxxxxxxxxx
                   <mailto:eclipselink-users@xxxxxxxxxxx>

                   https://dev.eclipse.org/mailman/listinfo/eclipselink-users

               _______________________________________________
               eclipselink-users mailing list
               eclipselink-users@xxxxxxxxxxx
               <mailto:eclipselink-users@xxxxxxxxxxx>

               https://dev.eclipse.org/mailman/listinfo/eclipselink-users



           _______________________________________________
           eclipselink-users mailing list
           eclipselink-users@xxxxxxxxxxx
           <mailto:eclipselink-users@xxxxxxxxxxx>

           https://dev.eclipse.org/mailman/listinfo/eclipselink-users



       _______________________________________________
       eclipselink-users mailing list
       eclipselink-users@xxxxxxxxxxx <mailto:eclipselink-users@xxxxxxxxxxx>

       https://dev.eclipse.org/mailman/listinfo/eclipselink-users



   _______________________________________________
   eclipselink-users mailing list
   eclipselink-users@xxxxxxxxxxx <mailto:eclipselink-users@xxxxxxxxxxx>

   https://dev.eclipse.org/mailman/listinfo/eclipselink-users



------------------------------------------------------------------------

_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/eclipselink-users


Back to the top