Native Java uses Mybatis to operate the database interface annotation form, which is similar to SpringBoot and does not need to manage the tool class of SqlSession connection

Hi I'm Shendi



https://sdpro.top/blog/html/article/1044.html



Description of Requirement

Using SpringBoot to integrate Mybatis has been used for a long time. When writing a program that does not have Spring but needs to operate the database, it will think of using the form of interface + annotation, which is more efficient and simple.

@AutowirtedIn Spring, you only need to write the interface mapping, and then use annotations to automatically load where you need to use it , and you can use it directly

When you don’t use Spring, you need to use SqlSessionFactory to use Mybatis, then get the SqlSession, then process the operation, and then close the SqlSession after the operation, which is very troublesome.

I originally thought that SqlSession.getMapper can use the interface instance all the time, but I found that after the connection is closed, the instance can no longer be used, and an error will be reported, so I changed the method to meet the requirements


The following is an introduction to the use process of Mybatis (interface + annotation form, xml form is not listed)



Dependency import

Two jar packages are required, one is jdbc driver, for example, mysql uses mysql-connector-java.jar, which can be found online

Also use a jar package of mybatis, you can download it directly from the github of mybatis

Using Maven is similar to introducing dependencies with SpringBoot. You can find the imported dependencies under Baidu. You need to introduce jdbc and mybatis



Mybatis configuration file writing

The format is xml, you can refer to the official document of Mybatis, it is in Chinese, the file name can be arbitrary, generally config.xml or mybatis-config.xml

Mybatis XML configuration file official document


Where do you want to put the configuration file, but generally put it in the src directory, because you need to find this configuration file, Mybatis provides a class

MyBatis includes a utility class called Resources that contains utility methods that make it easier to load resource files from the classpath or other locations.

As long as the program can find this file (the Resources class provided by Mybatis does not feel very easy to use, it can only be found in the src directory, and the subdirectories under src cannot be found...)



I posted my configuration here, you can watch the changes

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 这里的default指向的是使用的是哪个配置(id) 可以有多个 environment 指向多个数据库 -->
	<environments default="devMySql">
		<environment id="devMySql">
			<!-- 基于jdbc -->
			<transactionManager type="JDBC" />
			<!-- 数据源 配置一些数据库配置 type="POOLED" 使用MyBatis自带的连接池 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver" />
                <!-- 数据库 -->
				<property name="url" value="jdbc:mysql://localhost:3306/数据库?serverTimezone=UTC&amp;allowMultiQueries=true" />
				<property name="username" value="数据库用户名" />
				<property name="password" value="数据库密码" />
			</dataSource>
		</environment>
	</environments>
	<!-- 使用注解的方式 -->
	<mappers>
		<mapper class='shendi.TestMapper'/>
	</mappers>
</configuration>


Build SqlSessionFactory from XML

Every MyBatis-based application centers on an instance of SqlSessionFactory. An instance of SqlSessionFactory can be obtained through SqlSessionFactoryBuilder. The SqlSessionFactoryBuilder can build a SqlSessionFactory instance from an XML configuration file or a pre-configured Configuration instance.

SqlSessionFactory contains the information of the above configuration file, and the SqlSession used later is obtained from this class instance

It can be understood as a connection pool, and SqlSession is a connection


The construction method is as follows (official example)

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

The above resource represents the path of the configuration file (after the actual attempt, it indicates an error)

You can put the configuration file in the src folder, and then resource is directly the file name of the configuration file



Write the Mapper interface

There are three ways for Sql statements, one is the way of xml, the other is the way of direct interface + annotation, and the other is the way of interface + xml

For me, of course, the method of direct interface + annotation is easy to use, simple and efficient


You only need to write an interface, and then mark it on the function with annotations, so that the function can represent the corresponding SQL operation. Commonly used additions, deletions, changes, and queries

  • @Insert
  • @Delete
  • @Update
  • @Select

The code is directly posted here. After all, it is very, very simple, such as querying users based on id

public interface TestMapper {
    
    
    @Select("SELECT * FROM user WHERE id=${id} LIMIT 1")
    HashMap<String,String> userById(int id);
}

The above is just an example, you can also use the Bean way to get the return value instead of the HashMap way

Even if judgment, for loop, recursive query (@Results), etc.

For details, you can refer to other articles, the focus of this article is not here



It should be noted that during the compilation process of Java, the parameter name will be changed to arg0, arg1, etc., and the original name will be lost. Mybatis finds parameters through reflection. There are two solutions

1. Add the @Param annotation to the parameter to specify the name

@Select("SELECT * FROM user WHERE id=${id} LIMIT 1")
HashMap<String,String> userById(@Param("id") int id);

2. Specify the parameter parameter at compile time

javac -parameters Compile like this, but of course we can’t use the command line directly, usually use IDE, such as Eclipse, IDEA, etc.

  • IDEA
    • Click Preferences - Build, Execution, Deployment - Compiler - java Compiler
    • Enter in Additional command line parameters-parameters
  • Eclipse
    • Window - prefenrences - java - Compiler
    • 勾选 Strore infomation about method parameters(usable via reflection)

I prefer the second



configuration map

With Mapper, you need to configure it. In the part written in the initial configuration file, there are

<!-- 使用注解的方式 -->
<mappers>
    <mapper class='shendi.TestMapper'/>
</mappers>

Among them, class represents the class path, and TestMapper is the mapping class. If there are multiple mappers, just configure multiple mappers.


I also saw one on the official website, which can use the interface of the entire package as a mapping class, but I use it to report an error ...

<mappers>
	<package name="shendi"/>
</mappers>


use

Configuration, SQL, factory, all ok, the last step is to use, because it is too simple, here is the code directly

// 拿到工厂
String resource = "config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);

// 通过工厂获取SqlSession(连接)
SqlSession ss = ssf.openSession();
// 通过SqlSession获取接口映射实例
TestMapper tm = ss.getMapper(TestMapper.class);
// 直接使用接口就可以了
HashMap<String,String> user = tm.userById(1);
// 最后关闭SqlSession连接
ss.close();

When the SqlSession is closed, the interface instance obtained by getMapper can no longer be used. Otherwise, an error will be reported directly, so it cannot be stored as a class member.

It is too troublesome for me who is used to SpringBoot. There is only one instance of SpringBoot interface Mapper, and I don’t need to care about getting the connection and closing the connection, so I encapsulated a tool class



Tool class (no need to care about connection, easier to use)

Implemented using dynamic proxy, the core code is as shown in the figure below

insert image description here


Let me show how to use it first. It only takes one line of code to get user information like before.

HashMap<String,String> user = MapperUtil.TEST.userById(1);


The complete code of the tool class is as follows

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * 所有的映射对象.
 * <br>
 * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
 */
public class MapperUtil {
    
    

	public static final SqlSessionFactory SSF;
	
    // 映射类,全局放出去供调用
	public static final TestMapper TEST;
	
	static {
    
    
		SqlSessionFactory ssf = null;
        // 这个地方是找配置文件,用的自己写的,可以根据自己需求改掉
		try (FileInputStream fi = new FileInputStream(PathFactory.getPath(PathFactory.PROJECT, "/files/mybatis/config.xml"))) {
    
    
			ssf = new SqlSessionFactoryBuilder().build(fi);
		} catch (IOException e) {
    
    
			e.printStackTrace();
            // 日志,根据自己需求改掉
			Log.printErr("SqlSessionFactory创建失败,程序终止: %s", e.getMessage());
			System.exit(0);
		}
		SSF = ssf;
		ssf = null;
		
        // 拿到接口实例,后面多一个接口就照着多加一行就可以了
		TEST = getMapper(TestMapper.class);
	}
	
    // 动态代理,将接口实例的每个函数都代理了,执行前拿到SqlSession,执行完关闭SqlSession
	private static <T> T getMapper(Class<T> clazz) {
    
    
		@SuppressWarnings("unchecked")
		T obj = (T) Proxy.newProxyInstance(MapperUtil.class.getClassLoader(), new Class[] {
    
    clazz}, new InvocationHandler() {
    
    
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
				try (SqlSession ss = SSF.openSession()) {
    
    
					T mapper = ss.getMapper(clazz);
					
					Object result = method.invoke(mapper, args);
					
					return result;
				}
			}
		});
		
		return obj;
	}
	
}


supplement (important)

The addition, deletion and modification of Mybatis need to manually call SqlSession.commit to take effect (transaction), so the above tools need to be slightly modified

If you do not commit, no error will be reported, but the database has no data.

Change it to the following

private static <T> T getMapper(Class<T> clazz) {
    
    
    @SuppressWarnings("unchecked")
    T obj = (T) Proxy.newProxyInstance(MapperUtil.class.getClassLoader(), new Class[] {
    
    clazz}, new InvocationHandler() {
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
            SqlSession ss = SSF.openSession();
            try {
    
    
                T mapper = ss.getMapper(clazz);

                Object result = method.invoke(mapper, args);

                return result;
            } finally {
    
    
                ss.commit();
                ss.close();
            }
        }
    });

    return obj;
}

Specifically where the commit can be packaged by itself



END

Guess you like

Origin blog.csdn.net/qq_41806966/article/details/130191256