Multithreading Process

Liferay DXP 7.4 U10+ or Liferay Portal 7.4 GA14+

Your Upgrade Processes may involve making complex changes to large data sets. If performance is critical, use the processConcurrently() method in the UpgradeProcess class of your application. This method executes in multiple threads and can shorten your upgrade times.

Deploy Version 1.0.0

Start a new Liferay DXP instance by running

docker run -it -m 8g -p 8080:8080 liferay/dxp:2024.q2.11

Sign in to Liferay at http://localhost:8080 using the email address test@liferay.com and the password test. When prompted, change the password to learn.

Then, follow these steps:

  1. Download and unzip Multithreading Process.

    curl https://resources.learn.liferay.com/dxp/latest/en/building-applications/data-frameworks/upgrade-processes/liferay-j7z3.zip -O
    
    unzip liferay-j7z3.zip
    
  2. Move into the 1.0.0 directory, build and deploy.

    cd 1.0.0
    
    ../gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
    
    Note

    This command is the same as copying the deployed jars to /opt/liferay/osgi/modules on the Docker container.

  3. Confirm the deployment in the Liferay Docker container console.

    STARTED com.acme.j7z3.api_1.0.0 [1030]
    STARTED com.acme.j7z3.service_1.0.0 [1031]
    

Add Entries to the App

  1. Navigate to the Script console at Control PanelServer AdministrationScript.

  2. Add some entries by executing the following script.

    import com.acme.j7z3.service.J7Z3EntryLocalServiceUtil;
    
    import com.liferay.portal.kernel.dao.orm.QueryUtil;
    
    entry1 = J7Z3EntryLocalServiceUtil.createJ7Z3Entry(1);
    entry2 = J7Z3EntryLocalServiceUtil.createJ7Z3Entry(2);
    entry3 = J7Z3EntryLocalServiceUtil.createJ7Z3Entry(3);
    
    entry1.setName("able");
    entry2.setName("able");
    entry3.setName("able");
    
    J7Z3EntryLocalServiceUtil.addJ7Z3Entry(entry1);
    J7Z3EntryLocalServiceUtil.addJ7Z3Entry(entry2);
    J7Z3EntryLocalServiceUtil.addJ7Z3Entry(entry3);
    

    The J7Z3_J7Z3Entry table now has three entries.

    The three entries can been verified in the database table.

Execute the Upgrade

  1. Move into the 1.0.1 directory, build and deploy.

    cd ../1.0.1
    
    ../gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
    
  2. Navigate to the Gogo shell console at Control PanelGogo Shell.

  3. Verify that the 1.0.1 upgrade is available by entering the command upgrade:list com.acme.j7z3.service. The 1.0.1 version appears as a registered upgrade process in the output window.

  4. Execute the upgrade by entering the command upgrade:execute com.acme.j7z3.service. The output window shows that the upgrade was completed.

    Execute the upgrade and the output should display that the upgrade was completed.

  5. The J7Z3_J7Z3Entry table entries have now been updated.

    The updated entries can been verified in the database table.

Implement the Multithreading Method

In the UpgradeProcess class of your application, override the processConcurrently() method.

@Override
protected void doUpgrade() throws Exception {
	processConcurrently(
		"select j7z3EntryId, name from J7Z3_J7Z3Entry",
		resultSet -> new Object[] {
			resultSet.getLong("j7z3EntryId"), resultSet.getString("name")
		},
		columns -> {
			long j7z3EntryId = (Long)columns[0];

			try (PreparedStatement preparedStatement =
					connection.prepareStatement(
						"update J7Z3_J7Z3Entry set name = ? where " +
							"j7z3EntryId = ?")) {

				preparedStatement.setString(1, "baker");
				preparedStatement.setLong(2, j7z3EntryId);

				preparedStatement.executeUpdate();
			}
		},
		null);
}

The method has four parts:

  1. SQL Query - An SQL statement to query the database data.

  2. Gathering objects - Receive a result set and return an array of objects based on the result set.

  3. Processing objects - Execute business logic on the array of objects.

  4. Exception - Send a message if an exception occurs.

The sample project shows a simple example where the name field is processed and modified by the processConcurrently() method.

  1. select j7z3EntryId, name from J7Z3_J7Z3Entry - The SQL statement queries all the entries.

  2. resultSet -> new Object[] {
    		 resultSet.getLong("j7z3EntryId"), resultSet.getString("name")
    }
    

    The objects are gathered and stored in the resultSet array.

  3. columns -> {
    	    long j7z3EntryId = (Long)columns[0];
    
    	    try (PreparedStatement preparedStatement =
    			  connection.prepareStatement(
    				   "update J7Z3_J7Z3Entry set name = ? where " +
    					    "j7z3EntryId = ?")) {
    
    		     preparedStatement.setString(1, "baker");
    		     preparedStatement.setLong(2, j7z3EntryId);
    
    		     preparedStatement.executeUpdate();
    		 }
    	}
    

    For every j7z3EntryId, the name field is set as baker.

  4. The exception is set to null.

Alternate Implementations of processConcurrently()

There are two alternative signatures of the processConcurrently() method. The first uses Auto Batching for improved performance. In the J7Z3EntryUpgradeProcess.java file, replace the usage of processConcurrently() with the following code:

processConcurrently(
    "select j7z3EntryId, name from J7Z3_J7Z3Entry",
    "update J7Z3_J7Z3Entry set name = ? where j7z3EntryId = ?",
    resultSet -> new Object[] {
        resultSet.getLong("j7z3EntryId"), resultSet.getString("name")
    },
    (columns, preparedStatement) -> {
        long j7z3EntryId = (Long)columns[0];

        preparedStatement.setString(1, "baker");
        preparedStatement.setLong(2, j7z3EntryId);

        preparedStatement.addBatch();
    },
    null);

This signature has the following parameters:

  1. Like the original implementation, the first argument is a SQL query for obtaining the data to be processed.

  2. The second argument is the SQL query to insert, update, or delete data in a batch controlled by the method.

  3. The third argument is a function similar to the one in the original implementation:

    resultSet -> new Object[] {
    		 resultSet.getLong("j7z3EntryId"), resultSet.getString("name")
    }
    
  4. The fourth argument is also a similar function:

    (columns, preparedStatement) -> {
        long j7z3EntryId = (Long)columns[0];
        preparedStatement.setString(1, "baker");
        preparedStatement.setLong(2, j7z3EntryId);
        preparedStatement.addBatch();
    }
    
  5. The last argument is a String to be used in case an exception occurs.

Tip

Use this signature for simple upgrades that only update or insert SQL data. Use the previous signature for upgrades that do more than inserting data (e.g. deleting files or adding images).

The other signature passes an array as the source. See the BaseDBProcess javadocs to learn more.

Capabilities

Product

Contact Us

Connect

Powered by Liferay
© 2024 Liferay Inc. All Rights Reserved • Privacy Policy