Spring disable transaction for method in transactional class

Overview

In Spring @Transactional  annotation can be used to indicate method should be run in transaction.

When @Transactional  is placed on method, this method will be run with transaction. And if it’s placed on a class, all methods of this class will run within transaction.

 

In MVC style application, we usually wrap business logic into a separate service class annotated with @Service  and @Transactional , so every method will run within transaction. But how to disable transaction management for a specific single method in class which is annotated with @Transactional ?

 

Solution

Add following code before the method which you want to disable transaction management for.

Above code will tell Spring PlatformTransactionManager that transaction is not supported for this method, and transaction manager won’t create a new transaction if no transaction is existed.

And if a current transaction existed, exception will be thrown (An IllegalTransactionStateException with message “Existing transaction found for transaction marked with propagation ‘never'”).

 

java.lang.NoClassDefFoundError: java/util/Base64

Problem

In the following code, Base64 is used to encode AES encrypted data bytes to plain text for transfering via URL query parameter.

 

But when running this code on my coworker’s machine, following exception is thrown

 

It’s because that my execution environment is Java 8, but my coworker is using Java 7, and java.util.Base64 is introduced in Java 8.

 

Solution

To fix this issue, we can use Base64 class from Apache Commons Codec.

First add commons codec dependency to maven pom.xml file.

Next import Base64 class from commons-codec

 

Finally replace  Base64.getUrlEncoder().encodeToString to Base64.encodeBase64URLSafeString .

So the code will become like following

 

 

Method.invoke throws java.lang.IllegalArgumentException: wrong number of arguments

Background

There is a private downloadFile method of ZhangyoobaoLeshuaMerchantRegister class, the method signature is like following

A unit test case for this method need be created, in this test case we need pass a null value as parameter to test whether this method can handle null parameter. But a private method cannot be accessed from the instance level. The solution is to use reflection to get the method, and set its accessiblity to public temporarily (by using the Method.setAccessible method).

 

First let’s see some code

Above Java code is the unit test case, it uses reflection to get the private method downloadFile of class ZhangyoobaoLeshuaMerchantRegister, and set the access level of this method to public, then call it with parameters. (Note that if setAccessible is not called to set method access level, Method.invoke will throw IllegalAccessException.)

But running this line  result = method.invoke(register, null);  will got following error

It’s because the second argument of Method.invoke() is Object...  (An Object[] array), its elements are arguments passed in to the method. Calling Method.invoke() with null as second argument is actually trying to call the method with no arguments, but downloadFile method requires one parameter, so it definitely raises IllegalArgumentException.

 

Solution

To fix this issue, change following line

to

This will pass null as first argument for downloadFile method. new Object[]{null}  is correspondding to the Object...  argument of Method.invoke(), it’s expanded like calling downloadFile(null) .

 

Hibernate Query unexpected token: INTERVAL

I’m trying to fetch inventories which is opened at last month, for traditional SQL DATE_SUB function should be used here for date calculation. So the HQL is like following

 

But following error appeared when running it in Hibernate QL Query Consonle in Intellij IDEA

 

Unfortunatelly the INTERVAL keyword is not supported in HQL.

 

Solution

An alternative is to calculate the date in Java and pass the result dates to HQL as parameters. The code is like following

 

Implementation code for DateUtil.getLastMonthFirstDay and DateUtil.getLastMonthLastDay

 

 

 

Spring Cloud Zuul Ribbon custom rule not working

I need a custom Rule which can route requests to different service according to weight option (actually I want to deploy an A/B test service and only small percent of user will hit it), the code is like following

 

And following is application.yml configuration file

 

Adding breakpoints at constructor of ZoneAwareLoadBalancer and DynamicServerListLoadBalancer, then access the Zuul service via browser and I found ZoneAwareLoadBalancer constructor breakpoint is reached first.

Inspect the value clientConfig->properties->NFLoadBalancerClassName, its value is still “com.netflix.loadbalancer.ZoneAwareLoadBalancer”, and the passed in rule is not my configured rule as well.

This means my configuration didn’t work.

 

Even I changed the class name to an invalid value, there is no error

 

In Spring Cloud documentation, under Customizing the Ribbon Client using properties section, it’s saying

Starting with version 1.2.0, Spring Cloud Netflix now supports customizing Ribbon clients using properties to be compatible with the Ribbon documentation.

Checking maven pom file I found it’s using 1.1.0

 

Changing them to 1.2.0 and run again, following exception is thrown

 

It’s because NFLoadBalancerRuleClassName is aaa, changing it to a valid name and try it again, it works now

 

 

Java HTML Encoding (HTML Entities)

Some XSS attacks can be prevented by using HTML Encoding.

HTML encoding function is built into many languages, In .NET WebUtility.HtmlEncode  can do it, in PHP we can use htmlentites  function, in Python cgi.escape  can be used.

But there is no built-in function to do HTML Encode (or HTML Entities) in Java.

We can use Apache Commons Lang library to do this work.

 

Above code will output following result using HTML Encoding

<>

 

Note that not all XSS attacks can be prevented by HTML encoding (https://stackoverflow.com/questions/53728/will-html-encoding-prevent-all-kinds-of-xss-attacks).

Apache POI Sheet.getPhysicalNumberOfRows()

In the past I use following code to display first cell string of all rows.

But above code will be incorrect if there is empty row.

 

Following is explanation from POI official documentation

Sheet.getPhysicalNumberOfRows()

Returns the number of physically defined rows (NOT the number of rows in the sheet)

This method will ignore empty rows, e.g. if a sheet last row is at row 7, but second row is empty, getPhysicalNumberOfRows()  will return 6.

 

Solution

To get row count of a sheet (no matter the row is empty or not), we should use getLastRowNum()  method.

So above code can be changed to

Because getLastRowNum()  method returns 0-based row index, so we use i<=sheet.getLastRowNum()  as the loop condition.

Dom4j Writing File Not Working

Dom4j is a XML processing library which supports XPath, DOM, SAX,  JAXP. In this post we will see a problem that dom4j could not write document to file.

 

Following XML is sample data which we will save to file using dom4j.

To generate above XML and write it to file, following code will be used

Above code may generate an empty file with no content.

 

This is because the content is stored in buffer and not really written to file when calling write  method. To write to file, we should call flush  method or close  method.

So above code should be changed to

Or

 

Hibernate ConstraintViolationException

In following code (a JUnit test method) I’m trying to delete specified users from database

 

But when running above code, I got following exception:

org.hibernate.exception.ConstraintViolationException: could not execute batch

at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:129)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:132)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.doExecuteBatch(BatchingBatch.java:111)
at org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl.execute(AbstractBatchImpl.java:163)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.executeBatch(JdbcCoordinatorImpl.java:226)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:484)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
at com.dfweb.service.impl.SysUserServiceImplTest.testAddPermissions(SysUserServiceImplTest.java:77)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (db_df.permission_sys_user, CONSTRAINT FK_2l5pbq5juqkbrg6rtqes6w1el FOREIGN KEY (users_id) REFERENCES sys_user (id))
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2055)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1467)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1135)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:123)
… 41 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (db_df.permission_sys_user, CONSTRAINT FK_2l5pbq5juqkbrg6rtqes6w1el FOREIGN KEY (users_id) REFERENCES sys_user (id))
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1041)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4169)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2617)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2825)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2156)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2441)
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2007)
… 44 more

 

Reason

This is because there is an RESTRICT reference for foreign key of permission_sys_user that references id column of sys_user table.

So deleting the SysUser entity and keep its related permissions in permission_sys_user table will break the foreign key constraint of permission_sys_user table (references sys_user table with default RESTRICT reference option).

 

Solution

Following is SysUser class related source code

 

One possible solution is to remove its related records in permission_sys_user table when deleting SysUser entity, so change SysUser to following will fix this issue:

Above code adds CascadeType.REMOVE  for ManyToMany  annotation’s cascade  attribute. This will make deleting SysUser entity will trigger deleting related Permission entities.

java.lang.NoSuchMethodError: com.google.common.collect.ImmutableSet.toImmutableSet

Recently I tried to use Selenium for Web UI auto testing, but when running driver.get()  function, I got following error

java.lang.NoSuchMethodError: com.google.common.collect.ImmutableSet.toImmutableSet()Ljava/util/stream/Collector;

 

Following is full stacktrace

Starting ChromeDriver 2.29.461591 (62ebf098771772160f391d75e589dc567915b233) on port 20686
Only local connections are allowed.

java.lang.NoSuchMethodError: com.google.common.collect.ImmutableSet.toImmutableSet()Ljava/util/stream/Collector;

at org.openqa.selenium.remote.ProtocolHandshake.streamW3CProtocolParameters(ProtocolHandshake.java:238)
at org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:104)
at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:141)
at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:82)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:604)
at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:244)
at org.openqa.selenium.remote.RemoteWebDriver.<init>(RemoteWebDriver.java:131)
at org.openqa.selenium.remote.RemoteWebDriver.<init>(RemoteWebDriver.java:144)
at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:178)
at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:167)
at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:124)
at com.dfweb.service.impl.PaymentLogServiceImplTest.testCalcuateProfitWithDifferentAgentAndUserOfInventory(PaymentLogServiceImplTest.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Suppressed: java.io.IOException: Incomplete document
at com.google.gson.stream.JsonWriter.close(JsonWriter.java:527)
at org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:121)
… 37 more

 

Cause

I look through the com.google.guava jar source code, and found there is com.google.common.collect.ImmutableSet class existed, but there is no toImmutableSet() method defined.

It’s because ImmutableSet.toImmutableSet() method is introduced in Guava version 21.0, while I’m using version 20.0.

 

Solution

My current pom.xml

 

Change version to 21.0 and run mvn install  will fix this issue.