Java Xiaobai Practice Manual-Phase 5-MyBatis Framework (day02)

table of Contents

MyBatis

Use XML files to configure SQL statements

Questions about multiple parameters

@Param annotation

Dynamic SQL - foreach

About node configuration:

collection:

item :

separator:

Dynamic SQL - if

#{} and ${} placeholders in MyBatis

Simple principle:


MyBatis

Use XML files to configure SQL statements

Download http://doc.canglaoshi.org/config/Mapper.xml.zipand unzip to get SomeMapper.xml .

Create a mappers folder under the project's src/main/resources , copy SomeMapper.xml to this folder, and rename it to UserMapper.xml .

The name of the folder created in this step is customized and related to the subsequent configuration.

The name of the XML file in this step is customized and has nothing to do with any other configuration.

The UserMapper.xml added above is the file used to configure SQL statements, usually called the mapping file ! The following codes in this file are fixed and indispensable:

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

The root node of the file is a <mapper>node, namespacea property that must be configured , and the value of this property is the full name of the corresponding MyBatis interface file, for example:

< namespace="cn.tedu.mybatis.UserMapper">
</mapper>

Next, according to the type of the SQL statement to be executed, choosing <insert>, <delete>, <update>, <select>four nodes in a certain configuration to the SQL statement, the nodes must be configured with idattribute, the attribute value is the name of an abstract method interface , And then, you can write SQL statements inside the node:

<?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">
  
<!-- namespace属性:接口的全名 -->
<mapper namespace="cn.tedu.mybatis.UserMapper">

	<!-- id属性:抽象方法的名称 -->
	<insert id="insert">
		INSERT INTO t_user (
			username, password, age, phone, email
		) VALUES (
			#{username}, #{password}, #{age}, #{phone}, #{email}
		)
	</insert>

</mapper>

At present, the MyBatis framework does not know that there is a mappers folder, let alone which file configures the SQL statement! The location of the XML file that configures the SQL statement can be configured in jdbc.properties :

mybatis.mapper-locations=classpath:mappers/*.xml

The above code is used to indicate that " all XML files in the mappers folder under src/main/resouces are used to configure SQL statements". In subsequent use, you must also ensure that you will not add other files to the mappers folder. The role of the XML file!

Next, you should also read the above configuration information:

@Value("${mybatis.mapper-locations}")
private Resource[] mapperLocations;

And applied to SqlSessionFactoryBeanthe properties of the object:

@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(dataSource);
    bean.setMapperLocations(mapperLocations);
    return bean;
}

Finally, before running or testing, you need to pay attention: each abstract method can only configure SQL statements in one way, either using annotations or using XML files, not at the same time! Otherwise, an error will be reported. The key information of the error prompt is for example:

Caused by: java.lang.IllegalArgumentException: 
Mapped Statements collection already contains value for 
cn.tedu.mybatis.UserMapper.insert. please check file
[D:\eclipse-workspace-202003\mybatis\target\classes\mappers\UserMapper.xml] 
and cn/tedu/mybatis/UserMapper.java (best guess)

The above prompt Mapped Statements collection already contains value for cn.tedu.mybatis.UserMapper.insert.means "the cn.tedu.mybatis.UserMapper.insertmethod is mapped to the configuration of multiple SQL statements", and it is also recommended to check the UserMapper.xml and UserMapper.java files!

If you are configuring a <insert>node, and you need to obtain the ID value of the automatic number, you <insert>can continue to configure the attributes in the node, for example:

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO t_user (
        username, password, age, phone, email
    ) VALUES (
        #{username}, #{password}, #{age}, #{phone}, #{email}
    )
</insert>

When the data access that needs to be implemented is of the query type, one of the attributes (choose one) of or <select>must be configured in the node . If none is specified, the following error will occur:resultTyperesultMap

Caused by: org.apache.ibatis.executor.ExecutorException: 
A query was run and no Result Maps were found for the Mapped Statement 'cn.tedu.mybatis.UserMapper.count'.  
It's likely that neither a Result Type nor a Result Map was specified.

Among them, it resultTyperefers to the "type of the data that encapsulates the query result", which can also be understood as the "type of the return value of the abstract method". For example, it can be configured as:

<select id="count" resultType="java.lang.Integer">
	SELECT COUNT(*) FROM t_user
</select>

If the return value of an abstract method of a query is a Listcollection type, for example:

List<User> findAll();

When configuring <select>the resultTypeattribute, the attribute value must be the type of the element in the collection, for example:

<select id="findAll" resultType="cn.tedu.mybatis.User">
    SELECT * FROM t_user ORDER BY id
</select>

As for the resultMapattributes, they will be introduced later.

Note: After using this approach, you need to add a requirement "overloading is not allowed" to the definition of the abstract method name!

Questions about multiple parameters

When there is more than one parameter in the parameter list of the abstract method, the #{参数名称}parameter value cannot be accessed by using it directly when configuring the SQL statement !

Because the Java source file needs to be compiled into a bytecode file ( .class file ) before running , it will lose all partial names when compiling. Therefore, it will cause the problem that the original "parameter name" cannot be used at runtime. !

MyBatis allows you to use argas a prefix and add 0a name from the beginning (for example arg0,arg1 etc.) to indicate the name of the first parameter. Later, when configuring SQL statements, you can use, for example, #{arg0}to indicate the value of the first parameter of the abstract method. Use #{arg1}the value of the second parameter of the abstract method...and so on! In addition, you can also use paramas a prefix and add 1a name from the beginning (for example param1,param2 etc.). In specific use, argthe name of the paramseries and the name of the series can be used!

However, it is not convenient to use the names of the argand param series to express the semantics, and when the parameter list of the abstract method changes, the serial numbers in these names may also need to be adjusted!

@Paramannotation

If the annotation is @Param("person"), then the parameter will be named #{person} .

MyBatis provides @Paramannotations . This annotation is added before each parameter of the abstract method. You can specify the name in the annotation. Later, when configuring the SQL statement, the placeholder uses the name configured in the annotation!

When the @Param annotation is not used, the function parameter can only be one, and only #{} can be used in the query statement value , and the class to which it belongs must be Javabean,

While using @Param annotation, you can use multiple parameters, and you can use #{} or ${} when you use it in a query statement

For example, the code here @Param      

After using @Param, the order of parameters in the interface can also be disrupted, as long as the id is unique.
This annotation can be omitted when there is only one parameter
 

    /**
	 * 根据用户的id 修改用户的邮箱
	 * UPDATE t_user set email=#{email} WHERE id=#{id};
	 */
    //编译时-->局部变量的参数会丢失
	//@Update("UPDATE t_user SET email=#{arg0} WHERE id=#{arg0}")
	//@Update("UPDATE t_user SET email=#{param1} WHERE id=#{param2}")
	//Integer updateEmailById(String email,Integer id);
	//Integer updateEmailById(String arg0,Integer arg1);
	//Integer updateEmailById(String param1,Integer param2);
    
    @Update("UPDATE t_user SET email=#{email} WHERE id=#{id}")
    Integer updateEmailById(@Param("email") String email, @Param("id")Integer id);	

Dynamic SQL - foreach

Suppose there is a demand: delete user data in batches (delete several pieces of user data at once);

The SQL statement that needs to be executed is roughly:

DELETE FROM t_user WHERE id=? OR id=? OR id=?;
DELETE FROM t_user WHERE id IN (?,?,?);

Value! Only the data type and meaning of the above parameters can be determined!

The above functions will ultimately be up to the user (the user of the software) to determine the number of data to be deleted (the number of question marks), and which pieces of data are to be deleted (parameter values ​​corresponding to the question marks)! It will lead to "when the user's operations are different (the selected data to be deleted is different), the final SQL statement that needs to be executed is different"! The MyBatis framework provides a "dynamic SQL" mechanism to solve this problem!

Dynamic SQL : According to the different parameter values ​​provided by the user, the final SQL statement that needs to be executed can be different!

When it is necessary to achieve the above requirements for batch deletion, the abstract method can be designed as:

Integer deleteByIds(List<Integer> ids);

Or, it can be designed as (this case uses this)

Integer deleteByIds(Integer[] ids);

It can even be designed as:

Integer deleteByIds(Integer... ids);

When a variable parameter is processed, it is essentially data.

When configuring the SQL statement, you need <foreach>to configure the part of the SQL statement that needs to be generated through a loop through the node:

<delete id="deleteByIds">
    DELETE FROM t_user WHERE id IN (
    	<foreach collection="array" item="id" separator=",">
            #{id}
    	</foreach>
    )
</delete>

About <foreach>node configuration:

collection

Objects that need to be traversed, when the abstract method has only one parameter and no @Paramannotations are added , if the parameter type is a Listcollection, the value is taken list; if the parameter type is an array , the value is taken array; when the abstract method has more than one parameter , An @Paramannotation must be added , and the value is @Paramthe parameter value of the annotation configuration;

item :

For each element data in the traversal process, the current attribute can be a custom value to represent the name of the element data. When <foreach>using a #{}placeholder at the child level of a node , you can use this name to represent the data;

separator

Traverse the generated code fragments, the separator between each element data;

Exercise: Set the password of several users to a certain value according to their ids.

The SQL statement that needs to be executed is roughly:

UPDATE t_user SET password=? WHERE id IN (?,?,?)

The abstract method can be designed as:

Integer updatePasswordByIds(
    @Param("ids") List<Integer> ids, 
    @Param("password") String password
);    

Then, configure the SQL statement (configuration mapping):

<update id="updatePasswordByIds">
    UPDATE t_user SET password=#{password} WHERE id IN (
    	<foreach collection="ids" item="id" separator=",">
    		#{id}
    	</foreach>
    )
</update>

Dynamic SQL - if

 

When configuring SQL statements, you can add <if>nodes to determine parameter values, so as to determine whether a certain SQL statement fragment is included in the final executed SQL statement.

For example, there is:

List<User> find(
    @Param("where") String where,
    @Param("orderBy") String orderBy, 
    @Param("offset") Integer offset, 
    @Param("count") Integer count
);

It can be configured as:

<select id="find" resultType="cn.tedu.mybatis.User">
	SELECT * FROM t_user
    <if test="where != null">
    	WHERE ${where}
    </if>
    <if test="orderBy != null">
    	ORDER BY ${orderBy}
    </if>
    <if test="offset != null and count != null">
    	LIMIT #{offset}, #{count}
    </if>
</select>

Note: The above case is just a hypothetical code. In actual development, you must not use it like this!

Note: In dynamic SQL, the <if>node does not have a matching elsepart! If you must need to achieve if...elsethe grammatical effect, you can write two <if>nodes with completely opposite conditions , or you can configure through the <choose>,, <when>and <otherwise>nodes. The basic format is:

<choose>
	<when test="">
        满足条件时的SQL语句片段
    </when>
    <otherwise>
        不满足条件时的SQL语句片段
    </otherwise>
</choose>


#{} and ${} placeholders in MyBatis

MyBatis allows SQL statements used in configuring #{}and ${}placeholders these two formats is represented by a parameter value.

Simple principle :

In the original programming using JDBC technology, when writing SQL statements, you can write the position of the question mark ) , all use the #{ }format placeholder, and you cannot write the position of the question mark, you must use the ${ }format placeholder!

Use #{}formatted placeholders to only represent a certain value! When MyBatis is processed, it will be processed by pre-compilation, that is: first use the question mark to place the value represented by the placeholder, and submit the entire SQL statement to MySQL for compilation-related processing (including lexical analysis, semantic analysis) , Compile), when the compilation is completed, the value is substituted into the successfully compiled SQL statement and executed together. Simply put, when using #{}format placeholders, the final processing mechanism is precompiled! Therefore, when using this format of placeholders, you do not need to care about the data type of the value when writing SQL , and there is no risk of SQL injection !

Using ${}format placeholders, you can represent any fragment in the SQL statement! When MyBatis is processing, it will first splice${} the value corresponding to the format placeholder into the SQL statement, and then submit the SQL statement to MySQL for compilation-related processing, that is to say, the value of the format placeholder has been substituted before compilation In the SQL statement! Obviously, there is no (and it is impossible) to use precompilation when dealing with format placeholders! Therefore, when using this format of placeholders, you need to consider the data type in the SQL statement yourself . For example, the value of the string type needs to be enclosed in a pair of single quotes! In addition, there is the risk of SQL injection !${}${}

the difference:

  • In sql, when the incoming parameter is a character type, the # sign will be enclosed in single quotation marks, which will not cause sql injection
  • In sql, when the incoming parameter is a character type, the $ sign will not be enclosed with single quotation marks, which will cause sql injection
<select id="find" resultType="cn.tedu.mybatis.User">
		SELECT * FROM t_user
		<if test="where !=null">
			WHERE ${where}
		</if>
		<if test="orderBy != null">
			ORDER BY ${orderBy}
		</if>
		<if test="offset != null and count != null">
			LIMIT #{offset}, #{count}
		</if>
	
	</select>

 

 

Guess you like

Origin blog.csdn.net/c202003/article/details/107229695