Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The working process of Spring converting SQLException and embedding extension points

2025-01-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the knowledge of "the working process of Spring transforming SQLException and embedding extension points". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Fields SQLState and vendorCode in SQLException

I don't know if this is correct:

The source of SQLState is the compliance with the SQL standard. Although there are many vendors of SQL databases, as long as they all respect the SQL standard and JDBC specifications, different vendors are bound to return the same SQLState for the same error.

VendorCode is magical, and its corresponding getter method is called getErrorCode. But the vendor in the field name and the comments on its getter method have betrayed its meaning. VendorCode or errorCode is not part of the SQL standard, and different database vendors may return different errorCode for the same error.

How SQLErrorCodeSQLExceptionTranslator is registered as Bean

I had guessed that the default converter org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator in the SpringBoot environment would be registered with a method with @ Bean in some * AutoConfiguration after introducing the spring-boot-starter-jdbc dependency, and this method might also inject SQLErrorCodes registered as Bean through setter.

However, the latter, where the default SQLErrorCodes registration is Bean, I found, org\ springframework\ jdbc\ support\ sql-error-codes.xml, which registers and injects a series of attributes. However, I did not find the method mentioned in the previous paragraph to register it as Bean for SQLErrorCodeSQLExceptionTranslator and its parent class using IDEA's Alt + F7 to search where it is used. Let me know if any brother knows where it becomes Bean and joins the context. I used the hypothesis as a fact first.

Does the exception generated by batch processing only focus on the tail?

Please look at the first paragraph of the text.

Text

Org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator, which is a converter provided by Spring by default to convert various SQLException exceptions to Spring built-in uniform exception types. This article mainly uses it, or precisely its protected DataAccessException doTranslate (String task, @ Nullable String sql, SQLException ex) method, to take a look at its whole processing idea and the extension points that flow out to users.

The first paragraph SQLException sqlEx = ex; if (sqlEx instanceof BatchUpdateException & & sqlEx.getNextException ()! = null) {SQLException nestedSqlEx = sqlEx.getNextException (); if (nestedSqlEx.getErrorCode () > 0 | | nestedSqlEx.getSQLState ()! = null) {sqlEx = nestedSqlEx }}

The above is the first paragraph of the introduction to the doTranslate method, what it does is very simple, start to determine whether the exception is a BatchUpdateException type guess from the name, this exception will only be thrown during batch processing. And then sqlEx.getNextException (), we go back to the source code to see, there are these important related codes:

/ * Retrieves the exception chained to this * SQLException object by setNextException (SQLException ex). * * @ return the next SQLException object in the chain; * null if there are none * @ see # setNextException * / public SQLException getNextException () {return (next);} / * Adds an SQLException object to the end of the chain. * * @ param ex the new exception that will be added to the end of * the SQLException chain * @ see # getNextException * / public void setNextException (SQLException ex) {SQLException current = this; for (;;) {SQLException next=current.next; if (next! = null) {current = next; continue } if (nextUpdater.compareAndSet (current,null,ex)) {return;} current=current.next;}}

As soon as you read setNextException, you will know that if you are an old hand with linked lists, next must always point to the last element of the linked list.

If you look back at the converter code, what it does is: take out the last exception of the exception chain, and then judge that nestedSqlEx.getErrorCode () > 0 | | nestedSqlEx.getSQLState ()! = null. If passed, sqlEx is set to the last exception. After reading the code, you can see that sqlEx is the target that will be processed by the Spring transformation.

That is, as long as the SQLException at the end is a normal exception, Spring only cares about the exception at the end. Why is that?

Besides, why would SQLException build an anomaly chain? Isn't the top-level Exception already set up a mechanism like cause to implement abnormal nesting dolls?

The first extension point of the second paragraph / / First, try custom translation from overridden method. DataAccessException dae = customTranslate (task, sql, sqlEx); if (dae! = null) {return dae;}

The customTranslate method feels like an extension point just by hearing the name, so jump over and take a look at its code:

@ Nullable protected DataAccessException customTranslate (String task, @ Nullable String sql, SQLException sqlEx) {return null;}

The first embedded extension point appears. The existence of this method allows us to extend and load our own private work by inheriting the SQLErrorCodeSQLExceptionTranslator class. I guess the Bean method that registers SQLErrorCodeSQLExceptionTranslator into the environment should have @ ConditionalOnMissingBean. After we manually register the class that inherits SQLErrorCodeSQLExceptionTranslator as Bean, it will no longer register itself, thus realizing the private work of stealthily switching converters.

The second and third extension points of the third paragraph / / Next, try the custom SQLException translator, if available. SQLErrorCodes sqlErrorCodes = getSqlErrorCodes (); if (sqlErrorCodes! = null) {SQLExceptionTranslator customTranslator = sqlErrorCodes.getCustomSqlExceptionTranslator (); if (customTranslator! = null) {DataAccessException customDex = customTranslator.translate (task, sql, sqlEx) If (customDex! = null) {return customDex;}

SQLErrorCodes is the Bean class that was previously registered at org\ springframework\ jdbc\ support\ sql-error-codes.xml. And at the beginning of the xml file, they made it clear:

-Default SQL error codes for well-known databases.- Can be overridden by definitions in a "sql-error-codes.xml" file- in the root of the class path.

If you write a sql-error-codes.xml under the class path of your project, the default provided by Spring will be overwritten.

Notice sqlErrorCodes.getCustomSqlExceptionTranslator (). This step takes one of its members from a Bean, and this member is fully getter and obviously has setter, which means that it can be injected into a member Bean while the SQLErrorCodes is registered as Bean.

Combined with the comments in the sql-error-codes.xml header, users first write an implementation class with their own private SQLExceptionTranslator interface. Then create a new sql-error-codes.xml under the project class path and copy the content already provided by Spring. Then, register your private live SQLExceptionTranslator implementation class as Bean in xml, and add a member injection of customSqlExceptionTranslator to the copied content that registers SQLErrorCodes as Bean, using your own private live Bean, of course. This completes the expansion.

In fact, there is a third extension point. Notice the getSqlErrorCodes () in the first line, which has this getter, setter and members of sqlErrorCodes, where SQLErrorCodes is also the component that is later injected into ``SQLErrorCodeSQLExceptionTranslator. Then we can also customize an implementation class of SQLErrorCodes and register it as Bean instead of the default. The SQLErrorCodes` has been completely changed, and there must be no problem with private work.

The fourth paragraph of preparatory work

The fourth paragraph is also the last paragraph, which is relatively long and needs to be seen in pieces.

/ / Check SQLErrorCodes with corresponding error code, if available. If (sqlErrorCodes! = null) {String errorCode; if (sqlErrorCodes.isUseSqlStateForTranslation ()) {errorCode = sqlEx.getSQLState ();} else {/ / Try to find SQLException with actual error code, looping through the causes. / / E.g. Applicable to java.sql.DataTruncation as of JDK 1.6. SQLException current = sqlEx; while (current.getErrorCode () = = 0 & & current.getCause () instanceof SQLException) {current = (SQLException) current.getCause ();} errorCode = Integer.toString (current.getErrorCode ());}

Some preparatory work has been done in this paragraph.

/ * Set this property to true for databases that do not provide an error code * but that do provide SQL State (this includes PostgreSQL). * / public void setUseSqlStateForTranslation (boolean useStateCodeForTranslation) {this.useSqlStateForTranslation = useStateCodeForTranslation;} public boolean isUseSqlStateForTranslation () {return this.useSqlStateForTranslation;}

From the comments of the setUseSqlStateForTranslation method, we can infer that when isUseSqlStateForTranslation () returns true, the database vendor is the kind of JDBC execution error does not return errorCode only return SQLState, both should return false (neither return that is not a normal JDBC implementation). On this basis, go back to understand the code, then here is to get the error code corresponding to the most valuable exception to be converted that can indicate your error and set it to errorCode. The subsequent operations are based on this errorCode.

In the fourth paragraph, the fourth extension point if (errorCode! = null) {/ / Look for defined custom translations first. CustomSQLErrorCodesTranslation [] customTranslations = sqlErrorCodes.getCustomTranslations () If (customTranslations! = null) {for (CustomSQLErrorCodesTranslation customTranslation: customTranslations) {if (Arrays.binarySearch (customTranslation.getErrorCodes ()) ErrorCode) > = 0 & & customTranslation.getExceptionClass ()! = null) {DataAccessException customException = createCustomException (task, sql, sqlEx, customTranslation.getExceptionClass ()) If (customException! = null) {logTranslation (task, sql, sqlEx, true); return customException }}

This fourth extension point, CustomSQLErrorCodesTranslation [], which can be injected into SQLErrorCodes, takes a closer look at the class CustomSQLErrorCodesTranslation:

/ * * JavaBean for holding custom JDBC error codes translation for a particular * database. The "exceptionClass" property defines which exception will be * thrown for the list of error codes specified in the errorCodes property. * * @ author Thomas Risberg * @ since 1.1 * @ see SQLErrorCodeSQLExceptionTranslator * / public class CustomSQLErrorCodesTranslation {private String [] errorCodes = new String [0]; @ Nullable private Class exceptionClass

I didn't finish copying and pasting, because these are enough. Look at the notes:

The "exceptionClass" property defines which exception will be thrown for the list of error codes specified in the errorCodes property.

Back to the use of CustomSQLErrorCodesTranslation [] in paragraph 4:

For (CustomSQLErrorCodesTranslation customTranslation: customTranslations) {if (Arrays.binarySearch (customTranslation.getErrorCodes (), errorCode) > = 0 & & customTranslation.getExceptionClass ()! = null)

For each customTranslations in CustomSQLErrorCodesTranslation [], whether the current errorCode is within its processing scope (that is, its private String [] errorCodes array type has the same value as errorCode), if there is, and its private Class exceptionClass member is not null, it means that the current exception should be converted to an exception of the exceptionClass class.

So my motivation for writing this article at the beginning was geek time-playing Spring family buckets-to learn about Spring's JDBC abstract exception, and I wanted to figure it out. The course video is expanded by injecting custom CustomSQLErrorCodesTranslation [] `into the custom ``sql-error-codes.xml.

The fourth paragraph ends with / / Next, look for grouped error codes. If (Arrays.binarySearch (sqlErrorCodes.getBadSqlGrammarCodes (), errorCode) > = 0) {logTranslation (task, sql, sqlEx, false); return new BadSqlGrammarException (task, (sql! = null? Sql: ""), sqlEx);} else if (Arrays.binarySearch (sqlErrorCodes.getInvalidResultSetAccessCodes (), errorCode) > = 0) {logTranslation (task, sql, sqlEx, false); return new InvalidResultSetAccessException (task, (sql! = null? Sql: "), sqlEx);} / / there are several that have the same logic and similar logic will not be copied and pasted.

The general logic of this paragraph is the same as the previous section, and at the end, there is no room for expansion.

GetBadSqlGrammarCodes (), getInvalidResultSetAccessCodes () can also be changed through custom sql-error-codes.xml, but it is not necessary, because the types of exceptions they return are all written to death. In fact, this place is reserved for classic SQL errors by Spring, so let's not move. Just copy and paste this piece when you write your own sql-error-codes.xml. Here is the content of the corresponding MySQL database in the native sql-error-codes.xml of Spring:

MySQL MariaDB 1054,1064,1146 1062 630,839,840,893,1169,1215,1216,1217,1364,1451,1452,1557 1 1205,3572 1213

There is some code behind that is Spring can not handle the SQLException to do some logging, it is not worth talking about.

Extension point summary

The first extension point is the protected method. Users can add their own implementation to this method after inheritance, register their own implementation class as Bean, and combine the @ ConditionalOnMissingBean restriction when Spring registers Bean as the default implementation to achieve the extension point.

The essence of the subsequent extension points is to leave injectable members in the Bean of the default implementation. Users will bring their own private activities into the Bean with their own implementation logic by implementing a specific interface and registering it as Bean, combined with member injection.

This is the end of the introduction to "the working process of Spring transforming SQLException and embedding extension points". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report