Mapper interface development needs to follow the following specifications
Analyze how dynamic proxy objects are generated?
How is the analysis method performed?
Dao interface proxy
Case project skeleton
Continue to use the previous Mybatis traditional implementation Dao case
What is agent development?
To implement Dao layer in the traditional way, we should write both interfaces and implementation classes. The mybatis framework can help us omit the steps of writing Dao layer interface implementation classes. Programmers only need to write the interface. The mybatis framework itself can create the dynamic proxy object of the interface according to the definition of the interface.
The agent development mode of Mybatis is adopted to realize the development of DAO layer, which is the mainstream of entering the enterprise later.
Mapper interface development (equivalent to Dao interface) method only requires programmers to write mapper interface. Mybatis framework creates the dynamic proxy object of the interface according to the interface definition. The method body of the proxy object is the same as the implementation class method of Dao interface above.
Mapper interface development needs to follow the following specifications
(1) Mapper. The namespace in the XML file is the same as the fully qualified name (full class name) of the mapper interface.
(2) Mapper interface method name and mapper The id of each statement defined in the XML is the same.
(3) Input parameter type of mapper interface method and mapper The parameterType of each sql defined in XML is of the same type.
(4) Mapper interface method output parameter type and mapper The resultType of each sql defined in XML is the same.
(1) Mapper. The namespace in the XML file is the same as the fully qualified name (full class name) of the mapper interface.
StudentMapper. The namespace attribute value of the mapper tag in the XML Mapping configuration file is the full class name of the studentmapper interface.
(2) Mapper interface method name and mapper The id of each statement defined in the XML is the same.
In StudentMapper If the id attribute value in the XML is selectAll, the abstract method name in the StudentMapper interface should be consistent with it.
(3) Input parameter type of mapper interface method and mapper The parameterType of each sql defined in XML is of the same type.
In the StudentMapper interface, the parameter type of the abstract method is Integer, and the value of parameterType in its corresponding mapping configuration file is also Integer.
(Note: This was originally java.lang.Integer, but writing int framework in Mybatis will automatically convert it to Integer)
(4) Mapper interface method output parameter type and mapper The resultType of each sql defined in XML is the same.
Write StudentMapper interface
According to the above description, in general, it is shown in the following figure:
Case demonstration
Let's use the code of the previous case to modify it.
(1) Delete the interface implementation class of mapper layer
(2) Modify mapping profile
All codes are as follows:
(1) The Student class of the bean layer remains unchanged
(2) The StudentController class of the controller layer remains unchanged
(3) The mapper layer deletes the impl package (which contains its implementation class), and the StudentMapper interface remains unchanged
(4) The StudentServiceImpl of the service layer is the main modified object!!!
package com.itheima.service.impl; import com.itheima.bean.Student; import com.itheima.mapper.StudentMapper; import com.itheima.service.StudentService; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; /* Business layer implementation class */ public class StudentServiceImpl implements StudentService { @Override public List<Student> selectAll() { InputStream is = null; SqlSession sqlSession = null; List<Student> list = null; try { // 1. Load core configuration file is = Resources.getResourceAsStream("MyBatisConfig.xml"); // 2. Get SqlSessionFactory factory object SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); // 3. Obtain SqlSession object through factory object sqlSession = sqlSessionFactory.openSession(true); // ***4. Get the implementation class object of StudentMapper interface (generated dynamically through Mybatis) // getMapper() needs a class object of interface type // Equivalent to: StudentMapper mapper = new StudentMapperImpl(); // It's just that MyBatis obtains the implementation class object for us in a dynamic form StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // 5. Call the method by implementing the class object and accept the result list = mapper.selectAll(); } catch (Exception e) { e.printStackTrace(); } finally { // 6. Release resources if(sqlSession != null) { sqlSession.close(); } if(is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } // 7. Return results return list; } @Override public Student selectById(Integer id) { InputStream is = null; SqlSession sqlSession = null; Student stu = null; try { // 1. Load core configuration file is = Resources.getResourceAsStream("MyBatisConfig.xml"); // 2. Get SqlSessionFactory factory object SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); // 3. Obtain SqlSession object through factory object sqlSession = sqlSessionFactory.openSession(true); // ***4. Get the implementation class object of StudentMapper interface (generated dynamically through Mybatis) StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // 5. Call the method by implementing the class object and accept the result stu = mapper.selectById(id); } catch (Exception e) { e.printStackTrace(); } finally { // 6. Release resources if(sqlSession != null) { sqlSession.close(); } if(is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } // 7. Return results return stu; } @Override public Integer insert(Student stu) { InputStream is = null; SqlSession sqlSession = null; Integer result = null; try { // 1. Load core configuration file is = Resources.getResourceAsStream("MyBatisConfig.xml"); // 2. Get SqlSessionFactory factory object SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); // 3. Obtain SqlSession object through factory object sqlSession = sqlSessionFactory.openSession(true); // ***4. Get the implementation class object of StudentMapper interface (generated dynamically through Mybatis) StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // 5. Call the method by implementing the class object and accept the result result = mapper.insert(stu); } catch (Exception e) { e.printStackTrace(); } finally { // 6. Release resources if(sqlSession != null) { sqlSession.close(); } if(is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } // 7. Return results return result; } @Override public Integer update(Student stu) { InputStream is = null; SqlSession sqlSession = null; Integer result = null; try { // 1. Load core configuration file is = Resources.getResourceAsStream("MyBatisConfig.xml"); // 2. Get SqlSessionFactory factory object SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); // 3. Obtain SqlSession object through factory object sqlSession = sqlSessionFactory.openSession(true); // ***4. Get the implementation class object of StudentMapper interface (generated dynamically through Mybatis) StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // 5. Call the method through the implementation class object to accept the result result = mapper.update(stu); } catch (Exception e) { e.printStackTrace(); } finally { // 6. Release resources if(sqlSession != null) { sqlSession.close(); } if(is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } // 7. Return results return result; } @Override public Integer delete(Integer id) { InputStream is = null; SqlSession sqlSession = null; Integer result = null; try { // 1. Load core configuration file is = Resources.getResourceAsStream("MyBatisConfig.xml"); // 2. Get SqlSessionFactory factory object SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); // 3. Obtain SqlSession object through factory object sqlSession = sqlSessionFactory.openSession(true); // ***4. Get the implementation class object of StudentMapper interface (generated dynamically through Mybatis) StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // 5. Call the method by implementing the class object and accept the result result = mapper.delete(id); } catch (Exception e) { e.printStackTrace(); } finally { // 6. Release resources if(sqlSession != null) { sqlSession.close(); } if(is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } // 7. Return results return result; } }
(5) Core configuration file mybatisconfig XML invariant
(6) Mapping configuration file studentmapper XML needs to be changed
The namespace attribute under the mapper tag inside is changed to the full class name
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- mapper: Core root label namespace Attributes: namespaces --> <mapper namespace="com.itheima.mapper.StudentMapper"> <!-- select: Label of query function id Attribute: unique identification resultType Property: Specifies the result mapping object type parameterType Property: Specifies the type of parameter mapping object --> <select id="selectAll" resultType="student"> SELECT * FROM student </select> <select id="selectById" resultType="student" parameterType="int"> SELECT * FROM student WHERE id = #{id} </select> <!-- Returns a int Number of rows of type! Therefore, it can be omitted resultType But, SQL Parameters of the statement id,name,age It comes from students, so there should be parameterType --> <insert id="insert" parameterType="student"> INSERT INTO student VALUES (#{id},#{name},#{age}) </insert> <update id="update" parameterType="student"> UPDATE student SET name = #{name},age = #{age} WHERE id = #{id} </update> <!-- java.lang.Integer -> int--> <delete id="delete" parameterType="int"> DELETE FROM student WHERE id = #{id} </delete> </mapper>
Source code analysis
Analyze how dynamic proxy objects are generated?
Through the dynamic agent development mode, we only write an interface, not an implementation class. We finally get org. Org through the getMapper() method apache. ibatis. binding. Mapperproxy proxy object and then perform functions. MyBatis uses JDK's dynamic proxy technology to help us generate proxy implementation class objects. Thus, relevant persistence operations can be performed.
Get the implementation class object of StudentMapper interface (generated dynamically through Mybatis), and the bottom layer of getMapper() is as follows:
getMapper() is from SqlSeesion
<T> T getMapper(Class<T> var1);
The implementation class of sqlsession is DefaultSqlSession, in which the getMapper() method is rewritten
Note: the type inside is the class object of the StudentMapper interface, and this is the SqlSession object
Then the getMapper method of configuration is called
public <T> T getMapper(Class<T> type) { return this.configuration.getMapper(type, this); }
The getMapper() method in the configuration object calls the getMapper method of mapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return this.mapperRegistry.getMapper(type, sqlSession); }
The mapping proxy factory object mapperProxyFactory is obtained, and the factory calls newInstance again
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } else { try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception var5) { throw new BindingException("Error getting mapper instance. Cause: " + var5, var5); } } }
The mapping object mapperproxy is created, and the newInstance method is called to transfer the mapping proxy object mapperproxy
public T newInstance(SqlSession sqlSession) { MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache); return this.newInstance(mapperProxy); }
Finally, proxy is called Newproxyinstance (dynamic proxy provided by JDK)
The three parameters are: class loader, class type object array and proxy rule
protected T newInstance(MapperProxy<T> mapperProxy) { return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy); }
How is the analysis method performed?
When the dynamic proxy implementation class object executes the method, it finally calls mappermethod Execute () method. In this method, the switch statement is used to judge the operations of adding, modifying, deleting and querying according to the operation type. The last step returns to the most original SqlSession mode of MyBatis to execute adding, deleting, modifying and querying.
To execute the method, the dynamic agent first needs to use the invoke() method to get the mapperMethod object and call the execute method
Judge the type of method through switch! Finally, use the insert() method of SqlSession object!