📜 ⬆️ ⬇️

MyBatis and OSGi

Raising MyBatis


Few people assume what difficulties comprehend us on the path of introducing familiar technologies into new systems. One of the not-so-obvious difficulties is to make MyBatis friends with OSGi components. The most unusual difficulty is to hide your classes in the private part of the system. We do not want to put their objects out. As in the case of the phone we hide our SIM card and microSD card. Yes, we know that these things exist, but we don’t want to show anyone. The same with objects inside the OSGi component (bundle).

So, MyBatis, being a third-party library, cannot reach private objects. And we so want to close their secrets from everyone. Already hands itch and the chair creaks with impatience.

For normal operation, we need to learn how to cook MyBatis in parts. To begin with, the configuration is best configured in Java code, and not in the usual XML. Since we have OSGi, then access to the database is at the level of the OSGi service that implements the DataSource interface.

No one will tell you how to configure MyBatis to work with distributed transactions. They simply do not exist at the level of MyBatis Framework. It is necessary to use third-party systems. One thing we know for sure is that in this case you need to specify the transaction manager - ManagedTransactionFactory. For local transactions, select JdbcTransactionFactory.
')
Further the most interesting begins - these are classes superimposed on parameters and query results. We just hide these classes in the private part of OSGi components. Only one solution works here - assign an alias to each class. In this case, MyBatis will create a map of the classes. The keys will be the class alias, and the value will be the class. The configuration object, being created in the local loader, has normal access to the class from this map. Dynamic creation of an object of the desired class does not cause problems.

If we forget to assign an alias and in xml we specify the full path of the class being raised, MyBatis will not be able to raise this class. The factory of raising objects was created at the component level MyBatis (bundle) and does not have access to the class loader of our OSGi component. MyBatis tritely scolds us with an error at the stage of reading the results of executing a SQL query.

In MyBatis it is very convenient to use mapper interfaces. Here you can go two ways in the storage of SQL queries. You can store the request in the same XML file located in the same package where the interface lives. You can place a request in the annotation for the method being called. In the case of annotations, it is not necessary to prescribe pseudonyms to the classes. MyBatis, processing annotations, will do this work on their own. Oh, yes, you can make a complete leapfrog from XML files and annotations. In XML format there is a beautiful description of resultMap, and in annotations it corresponds to a complete mess in several methods. But in the annotation you can specify the name resultMap from the XML description. Also in the XML description there is an interesting functionality - import parts of SQL queries. It looks very nice when the methods receive the same objects and differ only in the parameters of the requests. In the annotations of such beauty does not repeat.

Yes, the MyBatis documentation recommends that all XML request files be registered in the configuration. I would say that it is necessary to register not XML, but interfaces (mapper). The system will figure out the XML file of the same name, next to the interface. But, as practice shows, this work can be postponed until better times. Let's just say, there is a chance that some requests will be performed quite rarely, that the procedure of raising these requests will be simply a waste of time and memory.

Performance


Here we smoothly turn to performance. Performance is not in terms of increasing database server performance. Performance in terms of the cost of storing and maintaining facilities and requests.

The simpler the objects transmitted in the parameters and the objects being formed in the queries, the faster the system raises them. As it was said, the process of raising can be postponed until better times. Thereby, reducing the initial configuration time of MyBatis. Moreover, the system does not spend much effort on the analysis of the structure of classes in the parameters and results of queries. It is enough to register the class once and the system will use the already prepared class decomposition map. The org.apache.ibatis.reflection.Reflector class is responsible for this. Hopefully, soon we will see a slightly different construction - org.apache.ibatis.reflection.ReflectorFactory.

What is ReflectorFactory new for?

As mentioned above, MyBatis remembers once the structure of an object. Well, if the objects are relatively small. If there are a lot of objects, well, let's say, several tens of megabytes, and they are used quite rarely. In this case, the performance of the system becomes its weak side. No one voluntarily cleans the memory of unnecessary objects. As a result, the JVM stores the structures of unused classes in a special Java 7 permgen section. ReflectorFactory will be used to clean this small piece of memory. Currently, the loss of MyBatis (Config) configuration results in the release of resources such as memory for storing SQL queries and mapping interfaces. MyBatis, forgetting the configuration, forgets about ReflectorFactory, thereby destroying the structure of the classes used.

What is the advantage of MyBatis from JPA or JDO implementations?

The main advantage is the ability to dynamically link a large number of objects to the database and the ability to forget these objects. Well, honestly. We know that JPA scans all classes for the presence of JPA Entity annotations. It is wonderful. What's next? Nothing further. There is no information on how the JPA will forget about these classes.

The author in JDO DataNucleuse did the implementation of dynamic class raising in OSGi. It was hinted that the system does not unload objects when the OSGi component is stopped. Years passed, and the unloading of registered objects did not appear. I think similar problems are present in other implementations of JPA.

Distributed transactions


The second topic covered in the article is distributed transactions. MyBatis does not have a distributed transaction support implementation.

Relatively recently, a very interesting and useful object appeared in MyBatis - SqlSessionManager. It allows you to use a single session to the database in one stream. Sessions in different threads do not overlap. The process of opening and closing a session is easily transferred to the annotation processors. This is used almost everywhere, in libraries based on Spring, CDI, Guice. Everywhere you can find transaction management annotations. And you can even see support for distributed transactions.

But there is one interesting trick in distributed transactions. This is the ability to start a new transaction within an already running transaction. Her name is "Required New". What happens to MyBatis? Everything is simple and sad. Working on the same thread, the SqlSessionManager object returns a previously opened connection. That is, opening a new transaction, we actually pick up the old connection in another transaction.

Abnormal termination of a nested transaction will not lead to rollback of actions, since all changes went as part of an external transaction. Accepting a nested transaction and rolling back the external one will result in the loss of the changes that were planned to be made in the nested transaction.

The project mybatis-guice proposed a solution to such a delicate problem. For the SqlSessionManager object, a special XAResource resource is generated. The transaction controller, TransactionManager, manages registered XA resources. Suspending an XA transaction switches information about a running session in the SqlSessionManager.

Why did this appear in mybatis-guice?

Again, OSGi’s fault. OSGi has its own specification, OSGi Blueprint. It is very similar to the Spring Framework. Their ancestor was one. For a number of reasons, these two frameworks conflict. Either the first or the second. No sweets. CDI Weld is a trick for web applications. Well, the developers of Weld did their best to write this useful solution exclusively for the web. So we have to find such a solution, which does not interfere with living among noisy and not very friendly neighbors. Guice is the system that normally gets along within several configurations that do not interfere with each other.

Let's just say that two configurations easily live in one component. One for working with local Jdbc transactions and one for working with distributed JTA transactions. The sets of classes are the same, there are no unnecessary annotations and xml descriptions. Even the configuration of MyBatis happens by the same method in the classroom.

I hope that soon this functionality will appear in the new version of mybatis-guice.

Source: https://habr.com/ru/post/257869/


All Articles