Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Recommended approach for avoiding a duplicate in server

    I have a simple table with its datasource, say having fields id, field1, field2, and field3.

    I got an insert request from the client. I want that no duplicates are inserted to the table regarding field1 and field2 only (so id and field3 are ignored).

    What approach would you recommend ?

    I got three ideas:
    1) Intercept the insert dsRequest in a DMI method, check for duplicates, and return an error status if the row already exists.
    2) Have a server-side validation in the datasource.ds.xml. Is this type of validation possible/feasible in this scenario?
    3) Have the DB guard or something similar, i.e. validation to the DB- level.

    I tend to prefer 1), since it is simple. But I would also like 2) if it is possible to do. If yes, what type of validation should the field/s have?

    Using Smartgwt 4.1 p.

    #2
    For #2, add a validator of type "serverCustom". These can be done as Velocity expressions, Server Script or DMI. Doing it as DMI would be essentially very similar to #1.

    You can *also* do #3 just as a failsafe, but you would still need either #1 or #2 in order to provide end users with a reasonable error message.

    Comment


      #3
      Originally posted by edulid View Post
      2) Have a server-side validation in the datasource.ds.xml. Is this type of validation possible/feasible in this scenario?
      I have SmartGWT 4.1p code for this (don't know the exact status), let me check on Monday.

      Best regards,
      Blama

      Comment


        #4
        Hi edulid,

        I have this code. I used it once for "giving meaningful error messages to the user" for Unique Constraint violations.
        I don't know the state of it as I never used it in production. It would be great if you could post your final code here if this is useful to you:

        ValidatorUniqueManyColumns.java:
        Code:
        import java.math.BigDecimal;
        import java.util.ArrayList;
        import java.util.Iterator;
        import java.util.List;
        import java.util.Map;
        
        import com.isomorphic.criteria.AdvancedCriteria;
        import com.isomorphic.criteria.Criterion;
        import com.isomorphic.criteria.DefaultOperators;
        import com.isomorphic.criteria.criterion.SimpleCriterion;
        import com.isomorphic.datasource.DSRequest;
        import com.isomorphic.datasource.DSResponse;
        import com.isomorphic.datasource.DataSource;
        import com.isomorphic.datasource.Validator;
        import com.isomorphic.log.Logger;
        
        public class ValidatorUniqueManyColumns {
        	Logger log = new Logger("com.isomorphic." + ValidatorUniqueManyColumns.class.getName());
        
        	public boolean condition(Object value, Validator validator, String fieldName, Map<String, Object> record, DataSource ds) throws Exception {
        
        		// We have additional values we need to check. These can - but most likely
        		// are not - in "values".
        		// Therefore we need to get them from the DB with the current PK.
        		@SuppressWarnings("unchecked")
        		Map<String, Object> dbValues = ds.fetchById(record.get(ds.getPrimaryKey()));
        
        		// The Multiple Unique Validator can validate uniqueness for one or many
        		// additional Columns.
        		// If it is one column, the type is String, otherwise it's List. We unify.
        		List<String> fieldsToCheck = new ArrayList<String>();
        
        		// The field we call the validator for
        		fieldsToCheck.add(fieldName);
        
        		if (validator.getObjectProperty("additionalUniqueField") instanceof String)
        			fieldsToCheck.add(validator.getProperty("additionalUniqueField"));
        		else if (validator.getObjectProperty("additionalUniqueField") instanceof List) {
        			@SuppressWarnings("unchecked")
        			Iterator<String> itr = validator.getListProperty("additionalUniqueField").listIterator();
        			while (itr.hasNext()) {
        				String additionalColumn = itr.next();
        				fieldsToCheck.add(additionalColumn);
        			}
        		}
        		// Now we have the fieldname to validate as well as all other fieldnames
        
        		// We build a list of Criterion that we'll feed to AdvancedCriteria later
        		// on. The values are taken either from record or from the DB.
        		ArrayList<Criterion> a = new ArrayList<Criterion>();
        
        		Iterator<String> itr = fieldsToCheck.listIterator();
        		while (itr.hasNext()) {
        			String columnToCheck = itr.next();
        			if (record.containsKey(columnToCheck))
        				// Taken from sent record
        				a.add(new SimpleCriterion(columnToCheck, DefaultOperators.Equals, record.get(columnToCheck)));
        			else
        				// Taken from DB
        				a.add(new SimpleCriterion(columnToCheck, DefaultOperators.Equals, dbValues.get(columnToCheck)));
        		}
        
        		Criterion[] myArray = new Criterion[a.size()];
        		final AdvancedCriteria ac = new AdvancedCriteria(DefaultOperators.And, a.toArray(myArray));
        		DSRequest r = new DSRequest(ds.getName(), DataSource.OP_FETCH) {
        			{
        				setAdvancedCriteria(ac);
        			}
        		};
        		DSResponse resp = r.execute();
        
        		// Now we got the element count with the values.
        		// Now two possibilities:
        		// Add -> We want a 0-count.
        		// Update -> We want either a 0-count or the same PK as before
        		if (resp.getTotalRows() == 0)
        			return true;
        		else if (resp.getTotalRows() >= 2)
        			return false;
        		else {
        			// Check for changed PK:
        			// If no old PK (=Add) and we find a row nevertheless, the Add would fail.
        			// The good case is handled above (at "resp.getTotalRows() == 0")
        			if (record.get(ds.getPrimaryKey()) == null)
        				return false;
        			// Compare old and new value (in case of an update)
        			BigDecimal oldvalue = BigDecimal.valueOf((Long) record.get(ds.getPrimaryKey()));
        			@SuppressWarnings("rawtypes")
        			BigDecimal newvalue = (BigDecimal) ((Map) resp.getRecords().get(0)).get(ds.getPrimaryKey());
        			boolean isSameRow = oldvalue.compareTo(newvalue) == 0;
        			return isSameRow;
        		}
        	}
        };
        .ds.xml usage:
        Code:
        <field name="POSITION" type="integer" required="true" editorType="SpinnerItem">
        	<validators>
        		<validator type="serverCustom">
        			<serverObject lookupStyle="new" className="com.mycompany.myproj.server.worker.validator.ValidatorUniqueManyColumns" />
        			<additionalUniqueField>TYPE</additionalUniqueField>
        			<errorMessage>Jede Position darf nur einmal vorkommen!</errorMessage>
        		</validator>
        		<validator type="integerRange" min="1" max="100" errorMessage="Bitte Sie eine Position zwischen 1 und 100 aus!" />
        	</validators>
        </field>
        Best regards,
        Blama

        Comment


          #5
          Hi Blama,

          thanks for the code, I will take a look.

          Comment


            #6
            Hi Blama, thanks for the code, I used it this way:

            Code:
            public boolean condition(Object value, Validator validator,
            			String fieldName, Map<String, Object> record, DataSource ds,
            			RPCManager rpcManager) throws Exception {
            
            		Map<String, Object> dbValues = new HashMap<String, Object>();
            
            		if (record.get(ds.getPrimaryKey()) != null) {
            			dbValues = ds.fetchById(record.get(ds.getPrimaryKey()));
            		}
            
            		List<String> fieldsToCheck = new ArrayList<String>();
            
            		fieldsToCheck.add(fieldName);
            
            		if (validator.getObjectProperty("additionalUniqueField") instanceof String)
            			fieldsToCheck.add(validator.getProperty("additionalUniqueField"));
            		else if (validator.getObjectProperty("additionalUniqueField") instanceof List) {
            			Iterator<String> itr = validator.getListProperty(
            					"additionalUniqueField").listIterator();
            			while (itr.hasNext()) {
            				String additionalColumn = itr.next();
            				fieldsToCheck.add(additionalColumn);
            			}
            		}
            
            		List<Criterion> criterions = new ArrayList<Criterion>();
            
            		Iterator<String> itr = fieldsToCheck.listIterator();
            		while (itr.hasNext()) {
            			String columnToCheck = itr.next();
            			if (record.containsKey(columnToCheck))
            				criterions.add(new SimpleCriterion(columnToCheck,
            						DefaultOperators.Equals, record.get(columnToCheck)));
            			else
            				criterions.add(new SimpleCriterion(columnToCheck,
            						DefaultOperators.Equals, dbValues.get(columnToCheck)));
            		}
            
            		final AdvancedCriteria ac = new AdvancedCriteria(DefaultOperators.And,
            				criterions.toArray(new Criterion[] {}));
            		DSRequest request = new DSRequest(ds.getName(), DataSource.OP_FETCH,
            				rpcManager);
            		request.setAdvancedCriteria(ac);
            
            		DSResponse response = request.execute();
            
            		/*
            		 * Now we got the element count with the values. Now two possibilities:
            		 * Add -> We want a 0-count. Update -> We want either a 0-count or the
            		 * same PK as before.
            		 */
            		if (response.getTotalRows() == 0) {
            			return true;
            		} else if (response.getTotalRows() >= 2) {
            			return false;
            		} else {
            			/*
            			 * Check for changed PK: If no old PK (=Add) and we find a row
            			 * nevertheless, the Add would fail. The good case is handled above
            			 * (at "resp.getTotalRows() == 0").
            			 */
            			if (record.get(ds.getPrimaryKey()) == null) {
            				return false;
            			}
            			/* Compare old and new value (in case of an update). */
            			BigDecimal oldvalue = BigDecimal.valueOf((Long) record.get(ds
            					.getPrimaryKey()));
            			BigDecimal newvalue = (BigDecimal) response.getDataMap().get(
            					ds.getPrimaryKey());
            			boolean isSameRow = oldvalue.compareTo(newvalue) == 0;
            			return isSameRow;
            		}
            	}

            Comment


              #7
              Hi

              When i tried to implement this code in my project there is no error in respective java file but while running code server i got the following error

              [ERROR] Line 242: No source code is available for type com.isomorphic.datasource.DataSource; did you forget to inherit a required module?
              [INFO] [ERROR] Line 246: No source code is available for type com.isomorphic.datasource.Validator; did you forget to inherit a required module?
              [INFO] [ERROR] Line 263: No source code is available for type com.isomorphic.criteria.Criterion; did you forget to inherit a required module?
              [INFO] [ERROR] Line 270: No source code is available for type com.isomorphic.criteria.criterion.SimpleCriterion; did you forget to inherit a required module?
              [INFO] [ERROR] Line 270: No source code is available for type com.isomorphic.criteria.DefaultOperators; did you forget to inherit a required module?
              [INFO] [ERROR] Line 277: No source code is available for type com.isomorphic.criteria.AdvancedCriteria; did you forget to inherit a required module?
              [INFO] [ERROR] Line 278: No source code is available for type com.isomorphic.datasource.DSRequest; did you forget to inherit a required module?
              [INFO] [ERROR] Line 283: No source code is available for type com.isomorphic.datasource.DSResponse; did you forget to inherit a required module?


              Note: isomorphic-core-rpc-5.0.jar is already available in maven dependency

              Comment


                #8
                These errors are not coming from SmatGWT. They are warnings from your IDE indicating that source code is not available for server-side classes, which is actually a normal and expected condition despite your IDE reporting it as an "error".

                Comment


                  #9
                  But due to this warnings the code server is not getting started so what can i do to over come this issue

                  Comment


                    #10
                    This warning would not prevent the code server from starting. Look at the complete log to find the actual problem.

                    Just in case: SmartGWT comes with classes that are for use on the server-side (com.isomorphic.*) and classes that are meant for use on the client side (com.smartgwt.*). Make sure you have read the QuickStart Guide in its entirety so you understand the distinction.

                    Comment


                      #11
                      Link succeeded
                      [INFO] Compilation succeeded -- 17.502s
                      [INFO] Compile completed in 17865 ms
                      [INFO] binding: user.agent=safari
                      [INFO] binding: compiler.useSourceMaps=true
                      [INFO] binding: locale=en
                      [INFO] Compiling module com.myproject.myapp.Application
                      [INFO] Validating units:
                      [INFO] Ignored 2 units with compilation errors in first pass.
                      [INFO] Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.
                      [INFO] [ERROR] Errors in 'file:/O:/eclipse/Workspace/myapp/src/main/java/com/myproject/myapp/client/pages/lab/mahesh/MaheshLab.java'
                      [INFO] [ERROR] Line 252: No source code is available for type com.isomorphic.datasource.DataSource; did you forget to inherit a required module?
                      [INFO] [ERROR] Line 256: No source code is available for type com.isomorphic.datasource.Validator; did you forget to inherit a required module?
                      [INFO] [ERROR] Line 273: No source code is available for type com.isomorphic.criteria.Criterion; did you forget to inherit a required module?
                      [INFO] [ERROR] Line 280: No source code is available for type com.isomorphic.criteria.criterion.SimpleCriterion; did you forget to inherit a required module?
                      [INFO] [ERROR] Line 280: No source code is available for type com.isomorphic.criteria.DefaultOperators; did you forget to inherit a required module?
                      [INFO] [ERROR] Line 287: No source code is available for type com.isomorphic.criteria.AdvancedCriteria; did you forget to inherit a required module?
                      [INFO] [ERROR] Line 288: No source code is available for type com.isomorphic.datasource.DSRequest; did you forget to inherit a required module?
                      [INFO] [ERROR] Line 293: No source code is available for type com.isomorphic.datasource.DSResponse; did you forget to inherit a required module?
                      [INFO] [ERROR] Compiler returned false
                      [ERROR] com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)
                      [ERROR] at com.google.gwt.dev.codeserver.Recompiler.compile(Recompiler.java:128)
                      [ERROR] at com.google.gwt.dev.codeserver.ModuleState.<init>(ModuleState.java:58)
                      [ERROR] at com.google.gwt.dev.codeserver.CodeServer.makeModules(CodeServer.java:120)
                      [ERROR] at com.google.gwt.dev.codeserver.CodeServer.start(CodeServer.java:95)
                      [ERROR] at com.google.gwt.dev.codeserver.CodeServer.main(CodeServer.java:71)
                      [ERROR] at com.google.gwt.dev.codeserver.CodeServer.main(CodeServer.java:49)
                      [INFO] ------------------------------------------------------------------------
                      [INFO] BUILD FAILURE
                      [INFO] ------------------------------------------------------------------------
                      [INFO] Total time: 23.283 s
                      [INFO] Finished at: 2014-12-16T17:24:01+05:30
                      [INFO] Final Memory: 25M/59M
                      [INFO] ------------------------------------------------------------------------
                      [ERROR] Failed to execute goal org.codehaus.mojo:gwt-maven-plugin:2.6.1:run-codeserver (default-cli) on project myapp: Command [[
                      [ERROR] C:\Program Files (x86)\Java\jdk1.7.0_72\jre\bin\java -Xmx512m -classpath O:\Eclipse\Workspace\myapp\target\classes;
                      O:\Eclipse\Workspace\myapp\src\main\java;O:\Maven\Repository\com\google\gwt\gwt-user\2.6.1\gwt-user-2.6.1.jar;O:\Maven\Reposit......

                      [ERROR] ]] failed with status 1
                      [ERROR] -> [Help 1]
                      [ERROR]
                      [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
                      [ERROR] Re-run Maven using the -X switch to enable full debug logging.
                      [ERROR]
                      [ERROR] For more information about the errors and possible solutions, please read the following articles:
                      [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

                      Comment


                        #12
                        myproject/myapp/client

                        Comment


                          #13
                          So i should replace com.isomorphic with com.smartgwt to over come this issue is it

                          Comment


                            #14
                            Hi mahbal,

                            no, you should use the code on the server and the server only (as isomorphic wrote: com.isomorphic.* is server only, com.smartgwt.* is client only). Do you use SmartGWT Pro/Power/EE-Eval/EE (I think so)?

                            Copy the java code into a validator folder under "server" and use like in the ".ds.xml usage" in the post.

                            Please also read the Quick Start Guide regarding SmartGWT Server Framework.

                            Best regards,
                            Blama

                            Comment


                              #15
                              Hi Blama/Isomorphic
                              First of all thanks for your help & support .
                              1)Since iam new to smartgwt iam not sure of how to call this function from my client side java file . If its possible can you show me how can i call this function from client side and how the "com.isomorphic" parameters for Validator ,DataSource can be declared in client side and how can i pass this to the function.


                              public boolean condition(Object value, Validator validator, String fieldName, Map<String, Object> record, DataSource ds)

                              2)Whether this function checks only when we do insert operation or whether i can use it to check manually whether validator is working or not before performing insert operation .

                              3)Can you post the full ds.xml file.



                              Thank's
                              Last edited by mahbal; 17 Dec 2014, 01:56.

                              Comment

                              Working...
                              X