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

How to develop MyBatis plug-ins

2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

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

This article introduces the knowledge of "how to develop MyBatis plug-ins". 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!

1.MyBatis plug-in interface

Even if you haven't developed MyBatis plug-ins, you can probably guess that MyBatis plug-ins work through interceptors. MyBatis framework has reserved relevant interfaces for plug-in development at the time of design, as follows:

Public interface Interceptor {Object intercept (Invocation invocation) throws Throwable; default Object plugin (Object target) {return Plugin.wrap (target, this);} default void setProperties (Properties properties) {/ / NOP}}

There are only three methods in this interface, the first method must be implemented, and the latter two methods are optional. The functions of the three methods are as follows:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Intercept: this is the specific interception method. When we customize the MyBatis plug-in, we generally need to rewrite this method, and all the work done by our plug-in is done in this method.

Plugin: the parameter target of this method is the object to be intercepted by the interceptor. Generally speaking, we do not need to override this method. The Plugin.wrap method automatically determines whether the signature of the interceptor matches the interface of the intercepted object, and if so, it intercepts the target object through a dynamic proxy.

SetProperties: this method is used to pass the parameters of the plug-in, which can be used to change the behavior of the plug-in. After we have defined the plug-in, we need to configure the plug-in. When configuring, we can set the relevant properties for the plug-in, and the set properties can be obtained through this method. The plug-in properties are set as follows:

2.MyBatis interceptor signature

Once the interceptor is defined, who will be intercepted?

This needs to be signed by the interceptor!

The interceptor signature is an annotation called @ Intercepts in which multiple signatures can be configured through @ Signature. The @ Signature annotation contains three attributes:

Type: the interfaces that the interceptor needs to intercept. There are four options: Executor, ParameterHandler, ResultSetHandler, and StatementHandler.

Method: the name of the method in the interface intercepted by the interceptor, that is, the name of the method in the first four interfaces, the interface and the method should correspond.

Args: the parameter type of the method intercepted by the interceptor, and the only method can be locked by the method name and parameter type.

A simple signature might look like this:

@ Intercepts (@ Signature (type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}) public class CamelInterceptor implements Interceptor {/ /.}

3. Intercepted object

According to the previous introduction, there are mainly four intercepted objects:

Executor

Public interface Executor {ResultHandler NO_RESULT_HANDLER = null; int update (MappedStatement ms, Object parameter) throws SQLException; List query (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; List query (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; Cursor queryCursor (MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException; List flushStatements () throws SQLException; void commit (boolean required) throws SQLException; void rollback (boolean required) throws SQLException CacheKey createCacheKey (MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql); boolean isCached (MappedStatement ms, CacheKey key); void clearLocalCache (); void deferLoad (MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class targetType); Transaction getTransaction (); void close (boolean forceRollback); boolean isClosed (); void setExecutorWrapper (Executor executor);}

The meanings of each method are as follows:

Update: this method is called when all INSERT, UPDATE, and DELETE are executed, and can be implemented through this method if you want to intercept these operations.

Query: this method is called when the SELECT query method is executed, and the method parameters carry a lot of useful information, which can be implemented through this method if you need to get it.

QueryCursor: this method is called when the return type of SELECT is Cursor.

FlushStatements: this method is triggered when the SqlSession method calls the flushStatements method or executes the interface method with the @ Flush annotation.

Commit: this method is triggered when the SqlSession method calls the commit method.

Rollback: this method is triggered when the SqlSession method calls the rollback method.

GetTransaction: this method is triggered when the SqlSession method acquires a database connection.

Close: this method is triggered after lazy loading to get a new Executor.

IsClosed: this method is triggered before lazy loading executes the query.

ParameterHandler

Public interface ParameterHandler {Object getParameterObject (); void setParameters (PreparedStatement ps) throws SQLException;}

The meanings of each method are as follows:

GetParameterObject: this method is triggered when the stored procedure is executed to process the output parameters.

SetParameters: this method is triggered when the SQL parameter is set.

ResultSetHandler

Public interface ResultSetHandler {List handleResultSets (Statement stmt) throws SQLException; Cursor handleCursorResultSets (Statement stmt) throws SQLException; void handleOutputParameters (CallableStatement cs) throws SQLException;}

The meanings of each method are as follows:

HandleResultSets: this method is triggered in all query methods (except for query methods with a return type of Cursor). Generally speaking, if we want to reprocess the query results, we can intercept this method.

HandleCursorResultSets: this method is triggered when the return type of the query method is Cursor.

HandleOutputParameters: this method is called when you use a stored procedure to process the output parameter.

StatementHandler

Public interface StatementHandler {Statement prepare (Connection connection, Integer transactionTimeout) throws SQLException; void parameterize (Statement statement) throws SQLException; void batch (Statement statement) throws SQLException; int update (Statement statement) throws SQLException; List query (Statement statement, ResultHandler resultHandler) throws SQLException; Cursor queryCursor (Statement statement) throws SQLException; BoundSql getBoundSql (); ParameterHandler getParameterHandler ();

The meanings of each method are as follows:

Prepare: this method is triggered before the database executes.

Parameterize: this method is executed after the prepare method and is used to process parameter information.

Batch: if defaultExecutorType= "BATCH" is configured in the entire play configuration of MyBatis, this method will be called when data manipulation is performed.

Update: this method is triggered when the update operation occurs.

Query: this method is triggered when the SELECT method is executed.

QueryCursor: this method is triggered when the SELECT method executes and the return value is Cursor.

When developing a specific plug-in, we should decide which method to intercept according to our own requirements.

4. Develop a paging plug-in

4.1 memory paging

MyBatis provides a difficult to use memory paging function, that is, all the data are queried at once, and then paged in memory, this paging method is very inefficient and basically useless, but if we want to customize the paging plug-in, we need to have a simple understanding of this paging method.

Memory paging is used as follows. First, add the RowBounds parameter to the Mapper, as follows:

Public interface UserMapper {List getAllUsersByPage (RowBounds rowBounds);}

Then define the relevant SQL in the XML file:

Select * from user

As you can see, when SQL is defined, you don't have to worry about paging at all. MyBatis will query all the data and do paging processing in memory.

Methods in Mapper are called as follows:

@ Test public void test3 () {UserMapper userMapper = sqlSessionFactory.openSession () .getMapper (UserMapper.class); RowBounds rowBounds = new RowBounds (1Magne2); List list = userMapper.getAllUsersByPage (rowBounds); for (User user: list) {System.out.println ("user =" + user);}}

When you build a RowBounds, you pass in two parameters, offset and limit, corresponding to the two parameters in the paged SQL. You can also build a RowBounds instance by RowBounds.DEFAULT. For a RowBounds instance built in this way, an offset of 0 is Integer.MAX_VALUE, which means no paging.

This is a very impractical memory paging feature provided in MyBatis.

Now that you know the memory paging that comes with MyBatis, let's take a look at how to customize the paging plug-in.

4.2 Custom paging plug-in

First of all, I would like to declare that here Brother Song takes you to customize the MyBatis paging plug-in, mainly to let your friends understand some of the rules and regulations of the custom MyBatis plug-in and understand the whole process of the custom plug-in. Paging plug-ins are not our goal, custom paging plug-ins are just to make your learning process more interesting.

Next, let's start the journey of custom paging plug-ins.

First of all, we need to customize a RowBounds, because the native RowBounds of MyBatis is paged in memory, and there is no way to get the total number of records (we also need to get the total number of records when paging query), so we customize PageRowBounds to enhance the native RowBounds feature, as follows:

Public class PageRowBounds extends RowBounds {private Long total; public PageRowBounds (int offset, int limit) {super (offset, limit);} public PageRowBounds () {} public Long getTotal () {return total;} public void setTotal (Long total) {this.total = total;}}

As you can see, the total field has been added to our custom PageRowBounds to hold the total number of records of the query.

Next, let's customize the interceptor PageInterceptor as follows:

@ Intercepts (@ Signature (type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) public class PageInterceptor implements Interceptor {@ Override public Object intercept (Invocation invocation) throws Throwable {Object [] args = invocation.getArgs (); MappedStatement ms = (MappedStatement) args [0]; Object parameterObject = args [1]; RowBounds rowBounds = (RowBounds) args [2] If (rowBounds! = RowBounds.DEFAULT) {Executor executor = (Executor) invocation.getTarget (); BoundSql boundSql = ms.getBoundSql (parameterObject); Field additionalParametersField = BoundSql.class.getDeclaredField ("additionalParameters"); additionalParametersField.setAccessible (true); Map additionalParameters = (Map) additionalParametersField.get (boundSql) If (rowBounds instanceof PageRowBounds) {MappedStatement countMs = newMappedStatement (ms, Long.class); CacheKey countKey = executor.createCacheKey (countMs, parameterObject, RowBounds.DEFAULT, boundSql); String countSql = "select count (*) from (" + boundSql.getSql () + ") temp"; BoundSql countBoundSql = new BoundSql (ms.getConfiguration (), countSql, boundSql.getParameterMappings (), parameterObject) Set keySet = additionalParameters.keySet (); for (String key: keySet) {countBoundSql.setAdditionalParameter (key, additionalParameters.get (key));} List countQueryResult = executor.query (countMs, parameterObject, RowBounds.DEFAULT, (ResultHandler) args [3], countKey, countBoundSql); Long count = (Long) countQueryResult.get (0) ((PageRowBounds) rowBounds) .setTotal (count);} CacheKey pageKey = executor.createCacheKey (ms, parameterObject, rowBounds, boundSql); pageKey.update ("RowBounds"); String pageSql = boundSql.getSql () + "limit" + rowBounds.getOffset () + "," + rowBounds.getLimit (); BoundSql pageBoundSql = new BoundSql (ms.getConfiguration (), pageSql, boundSql.getParameterMappings (), parameterObject) Set keySet = additionalParameters.keySet (); for (String key: keySet) {pageBoundSql.setAdditionalParameter (key, additionalParameters.get (key));} List list = executor.query (ms, parameterObject, RowBounds.DEFAULT, (ResultHandler) args [3], pageKey, pageBoundSql); return list } / / return the result return invocation.proceed () directly without paging;} private MappedStatement newMappedStatement (MappedStatement ms, Class longClass) {MappedStatement.Builder builder = new MappedStatement.Builder (ms.getConfiguration (), ms.getId () + "_ count", ms.getSqlSource (), ms.getSqlCommandType ()) ResultMap resultMap = new ResultMap.Builder (ms.getConfiguration (), ms.getId (), longClass, new ArrayList (0)). Build () Builder.resource (ms.getResource ()) .fetchSize (ms.getFetchSize ()) .statementType (ms.getStatementType ()) .timeout (ms.getTimeout ()) .parameterMap (ms.getParameterMap ()) .resultSetType (ms.getResultSetType ()) .cache (ms.getCache ( ) .flushCacheRequired (ms.isFlushCacheRequired ()) .useCache (ms.isUseCache ()) .resultMaps (Arrays.asList (resultMap)) If (ms.getKeyProperties ()! = null & & ms.getKeyProperties (). Length > 0) {StringBuilder keyProperties = new StringBuilder (); for (String keyProperty: ms.getKeyProperties ()) {keyProperties.append (keyProperty) .append (",");} keyProperties.delete (keyProperties.length ()-1, keyProperties.length ()) Builder.keyProperty (keyProperties.toString ());} return builder.build ();}}

This is our definition of the core code today, involving the knowledge of Brother Song to give you an analysis.

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

First, the interceptor signature is configured through the @ Intercepts annotation. From the definition of @ Signature, we can see that the interceptor is the Executor#query method, which has an overloaded method that specifies the method parameters through args, and then locks the overloaded method (in fact, we cannot intercept another overloaded method of this method, which is called internally by MyBatis, which is not discussed here).

After intercepting the query operation, the next operation is mainly done in the PageInterceptor#intercept method, whose parameters contain a lot of information about the intercepted object.

Get the parameters of the intercept method through invocation.getArgs (), and you get an array, which is normally 4 in length. The first item of the array is a MappedStatement, and the various operation nodes and SQL that we define in Mapper.xml are encapsulated into MappedStatement objects; the second item of the array is the specific parameters of the intercepted method, that is, the method parameters that you define in the Mapper interface The third item of the array is a RowBounds object. We do not necessarily use a RowBounds object when defining methods in the Mapper interface. If we do not define a RowBounds object, the system will provide us with a default RowBounds.DEFAULT; array. The fourth item is a ResultHandler that handles the return value.

Next, determine whether the rowBounds object extracted in the previous step is not RowBounds.DEFAULT; if it is RowBounds.DEFAULT, the user does not want to page; if not, it means the user wants to page; if the user does not want to page, then directly execute the final return invocation.proceed (); just let the method continue to go down.

If paging is needed, take out the actuator Executor, BoundSql, and the extra parameters saved in the BoundSql by reflection from the invocation object (this parameter may exist if we use dynamic SQL). The Sql we executed and the related parameters are encapsulated in BoundSql.

Next, determine whether rowBounds is an instance of PageRowBounds. If so, you also want to query the total number of records in addition to paging query. If not, it means that rowBounds may be an instance of RowBounds. In this case, you only need to be paged without querying the total number of records.

If you need to query the total number of records, first call the newMappedStatement method to construct a new MappedStatement object whose return value is of type Long. Then create the CacheKey of the query and the countSql of the stitching query respectively, then build the countBoundSql according to the countSql, and then add the additional parameters to the countBoundSql. Finally, the query operation is completed by the executor.query method, and the query result is assigned to the total attribute in PageRowBounds.

After the introduction of step 7, the paging query is very simple. We won't go into details here. The only thing we need to emphasize is that when we start this paging plug-in, the MyBatis native RowBounds memory pages will become physical pages. The reason is that we modified the query SQL here.

Finally, the query result is returned.

In the previous code, we reorganized SQL in two places, one is when querying the total number of records, and the other is when paging, we all get the SQL in Mapper.xml through boundSql.getSql () and modify it. Some friends don't pay attention when writing SQL in Mapper.xml, and may be added at the end; this will lead to errors in the operation of the SQL reassembled by the paging plug-in, which should be noted. The same is true of other MyBatis paging plug-ins that Songge saw on GitHub. You can't have a SQL ending in Mapper.xml.

After that, our paging plug-in is defined successfully.

5. test

Next, let's do a simple test of our paging plug-in.

First, we need to configure the paging plug-in in the global configuration as follows:

Next, we define the query interface in Mapper:

Public interface UserMapper {List getAllUsersByPage (RowBounds rowBounds);}

Next, define UserMapper.xml as follows:

Select * from user

Finally, we conduct a test:

@ Test public void test3 () {UserMapper userMapper = sqlSessionFactory.openSession () .getMapper (UserMapper.class); List list = userMapper.getAllUsersByPage (new RowBounds (1Magne2)); for (User user: list) {System.out.println ("user =" + user);}}

Here, when we use the RowBounds object in the query, we will only paginate, not count the total number of records. It should be noted that the paging is no longer a memory paging, but a physical paging, as we can see from the printed SQL, as shown below:

As you can see, paging has already been carried out at the time of the query.

Of course, we can also use PageRowBounds for testing, as follows:

@ Test public void test4 () {UserMapper userMapper = sqlSessionFactory.openSession () .getMapper (UserMapper.class); PageRowBounds pageRowBounds = new PageRowBounds (1,2); List list = userMapper.getAllUsersByPage (pageRowBounds); for (User user: list) {System.out.println ("user =" + user);} System.out.println ("pageRowBounds.getTotal () =" + pageRowBounds.getTotal ());}

At this point, we can get the total number of records through the pageRowBounds.getTotal () method.

This is the end of "how to develop MyBatis plug-ins". 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