MyBatis框架:全局配置文件的属性,映射文件,映射文件的参数问题

全局配置文件的属性

properties属性(基本不用)

	<!-- mybatis可以使用properties来引入外部properties配置文件的内容
		resource:引入类路径下的资源
		url:引入网络路径下的资源,或者磁盘上绝对路径
	 -->
	<properties resource="jdbcconfig.properties"></properties>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=1234

settings设置
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
比如里面的mapUnderscoreToCamelCase就是是否开启自动驼峰命名规则(camel case)映射,就是从数据库列名THE_NAME到java属性名theName的类映射,使用查询的时候能自动进行匹配,注意下划线后面的字母还是大写的,这是驼峰命名规则

<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>

typeAliases别名处理器

	<!-- 
		typeAliases:别名处理器,可以为我们的Java类型起别名(别名不区分大小写)
		typeAlias是为某一个Java类型起别名
			type:指定要起别名的类型全限类名,默认是类名的小写
			alias:指定新的别名
		
		package:为某个包下的所有类批量起别名
				name:指定包名(为当前包以及下面的所有子包的每一个类都起一个默认别名)
		
		但是上面的还有一个问题,就是这个包和它的子包有一个类的名字是一样的
		这样的话,mybatis就会报错,所以我们还有一个方法起别名
		
		使用@Alias注解起别名
		批量起别名的情况下,使用注解为某个类型指定新的别名 
	 -->
	<typeAliases>
		<typeAlias type="bean.Emp" alias="emp"/>
		<package name=""/>
	</typeAliases>

environments属性

	<!-- 
		environments:多种环境,mybatis可以配置多种环境,default是指定使用某种环境,达到快速切换环境的效果
		environment:配置一个具体的环境信息,id是这个环境的唯一标识
				transactionManager:事务管理器
					type:事务管理器的类型,:
							JDBC:JdbcTransactionFactory
							MANAGED:ManagedTransactionFactory
							还可以自定义事务管理器:只要实现TransactionFactory接口,type是指定它的全限类名就行,
												   或者参考上面两个这么写的就怎么写
				
				dataSource:数据源
						type:数据源的类型,;
								JNDI:JndiDataSourceFactory
								POOLED:PooledDataSourceFactory
								UNPOOLED:UnpooledDataSourceFactory
						还可以自定义数据源:实现DataSourceFactory接口就行,type就是它的全限类名
	 -->
	<environments default="development_mysql">
		<environment id="development_mysql">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>

databaseIdProvider属性
全局配置

	<!-- 
		databaseIdProvider:让mybatis支持多数据库厂商,让mybatis的移植性更好
		type="DB_VENDOR":作用是得到数据库厂商的标识(就是驱动,根据getDatabaseProductName()的方法得到)
		mybatis就能根据数据库厂商标识来执行不同的sql
		比如MySQL	Oracle	SQL Server
		可以利用下面这个标签给数据库厂商起一个好用的别名
		<property name="" value=""/>
		然后就是映射文件的配置,
		在select标签里面的databaseId="mysql"属性表明这个查询是使用哪一个数据库的
	 -->
	<databaseIdProvider type="DB_VENDOR">
		<property name="MySQL" value="mysql"/>
		<property name="Oracle" value="oracl"/>
	</databaseIdProvider>

映射文件配置
如果这里是在mysql环境下,会加载有mysql标识的和没有任何标识的语句,如果执行的时候优先使用带mysql标识的sql

<mapper namespace="bean.EmpMapper">
	<select id="getEmpById" resultType="bean.Emp" > 
		select eid,ename name,email,gender from emp where eid= #{id}  
	</select>
	<!-- 这是两个版本数据库的查询,上面是mysql,下面的是oracle的查询 -->
	<select id="getEmpById" resultType="bean.Emp" databaseId="mysql"> 
		select eid,ename name,email,gender from emp where eid= #{id}  
	</select>
	<select id="getEmpById" resultType="bean.Emp" databaseId="oracle"> 
		select eid,ename name,email,gender from emp where eid= #{id}  
	</select>
</mapper>

这里也配置了两个环境

	<environments default="development_mysql">
		<environment id="development_mysql">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
		
		<environment id="development_oracle">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${orcl.driver}" />
				<property name="url" value="${orcl.url}" />
				<property name="username" value="${orcl.username}" />
				<property name="password" value="${orcl.password}" />
			</dataSource>
		</environment>
		
	</environments>

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=1234

orcl.driver=oracle.jdbc.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=1234

mappers映射注册

	<!-- 将写好的sql映射文件注册到全局配置文件中 -->
	<!-- 
		mappers:将sql映射注册到全局文件中
			mapper:注册一个sql映射
			注册配置文件
					resource:引用类路径下的sql映射文件
					url:引用网络路径或者磁盘路径的sql映射文件
			注册接口:
				class:引用接口,就是注册接口,而不是映射文件了
					1.有sql映射文件的,映射文件名必须和接口同名,而且是要放在接口同一目录下					
					2.mybatis也支持没有sql映射文件的,就是所有的sql都是写在接口的注解上面
					上面两种方法的推荐:
									比较重要的,复杂的Dao接口写在映射文件上
									不重要,简单的Dao接口为了开发方便可以写在注解上面
					3.第三种就是批量注册了,使用package标签
						注意的是批量注册会找到包下的所有类
						如果是接口注释的我们还能理解,
						但是如果是文件注册的mybatis是怎么找到映射文件的呢?
						所以这个批量注册还是有要求的,如果有映射注册,
						就必须接口类和映射文件在同一个包下
						注意你可能觉得java源文件和xml文件放在一起,这样不好看
						所以你可以在资源文件夹conf的目录下新建一个和接口类一模一样的包名
						那么在编译的时候,因为src和conf都是资源文件夹,所以会被解析到一个文件夹里面
						那么这两个文件夹就是同一个文件夹了,只是在开发的时候视觉上不是同一个文件夹
	 -->
	<mappers>
		<mapper resource="EmpMapper.xml" />
		<mapper class="bean.EmpMapperAnnotation"/>
		<package name="bean"/>
	</mappers>

注意那些文件路径的问题, 比如什么java源文件,批量注册的包路径名,包之间使用.(点号), 而什么xml文件的路径名,包之间使用的是/(斜杠)

映射文件

增删改查
下面的代码中,增删改的操作做,需要返回值就直接是返回Integer/Long/Boolean这些类型,这些返回值mybatis已经定义好了,会自动帮你封装,只要返回不是0行数据,Boolean都是True

package bean;

public interface EmpMapper
{
	public Emp getEmpById(Integer id);
	public long addEmp(Emp emp);
	public boolean updateEmp(Emp emp);
	public long deleteEmpById(Integer eid);
}

映射文件写法

	<!-- 
		public long addEmp(Emp emp);
		public boolean updateEmp(Emp emp);
		public long deleteEmpById(Integer eid);
	 -->
	 <insert id="addEmp" parameterType="bean.Emp">
	 		insert into emp (ename,email,gender) values(#{ename},#{email},#{gender})
	 </insert>
	 
	 <update id="updateEmp">
	 		update emp set ename=#{ename},email=#{email},gender=#{gender}
	 		where eid=#{eid}
	 </update>
	 
	 <delete id="deleteEmpById">
	 		delete from emp where eid=#{eid}
	 </delete>

	@org.junit.Test
	public void Test4() throws IOException
	{
		String resource="mybatis.xml";
		InputStream resourceAsStream = Resources.getResourceAsStream(resource);
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
		SqlSession openSession = sqlSessionFactory.openSession();
		try
		{
			EmpMapper empMapper = openSession.getMapper(EmpMapper.class);
			//Emp emp=new Emp(2, "jane", "jane.com", "男");
			//empMapper.addEmp(emp);
			//boolean updateEmp = empMapper.updateEmp(emp);
			//System.out.println(updateEmp);
			long deleteEmpById = empMapper.deleteEmpById(2);
			System.out.println(deleteEmpById);
			//必须手动提交
			openSession.commit();
		}finally
		{
			openSession.close();
		}
	}

映射文件的insert获取主键的值
和JDBC一样,mybatis也是通过使用statement.getGenreatedKeys()来获取,需要设置两个参数
useGeneratedKeys=“true” keyProperty=“eid”
keyProperty="eid"是将返回的主键赋值给哪一个变量,将这个返回的值封装给JavaBean的哪一个属性

	 <insert id="addEmp" parameterType="bean.Emp" useGeneratedKeys="true" keyProperty="eid">
	 		insert into emp (ename,email,gender) values(#{ename},#{email},#{gender})
	 </insert>

			EmpMapper empMapper = openSession.getMapper(EmpMapper.class);
			Emp emp=new Emp(null, "jane", "jane.com", "男");
			empMapper.addEmp(emp);
			System.out.println(emp.getEid());

Oracle的处理方法

	 <!-- 
	 	获取非自增的主键的值:
	 		像Oracle是不支持自增的,Oracle使用的是序列来模拟自增的
	 		每一次插入的数据的主键都是从序列中拿到的值,那么我们如何获取这个值呢?
	 		
	 		mybatis支持使用属性selectKey来获取主键:
	 				keyProperty:查出的主键值封装给JavaBean的哪一个属性
	 				order:设置当前的selectKey的sql语句在插入sql语句的前后运行,有两个参数
	 						BEFORE:当前的selectKey的sql语句在插入sql语句的前运行
	 								运行顺序:
	 										先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
											在运行插入的sql;就可以取出id属性对应的值
	 						AFTER:当前的selectKey的sql语句在插入sql语句的后运行
	 								运行顺序:
	 										先运行插入的sql(从序列中取出新值作为id);
											再运行selectKey查询id的sql;
	 				resultType:查出的数据的返回值类型
	  -->
	 <!-- BEFORE的编写 -->
	 <insert id="addEmp" parameterType="bean.Emp"  databaseId="oracle">
	 		<selectKey keyProperty="eid" order="BEFORE" resultType="Integer">
	 			select EMPLOYEES_SEQ.nextval from dual 
	 		</selectKey>
	 		<!-- 插入时的主键时从序列中拿到的 -->
	 		insert into emp (eid,ename,email,gender) values(#{eid},#{ename},#{email},#{gender})
	 </insert>
	 
	 <!-- AFTER的编写 -->
	 <insert id="addEmp" parameterType="bean.Emp"  databaseId="oracle">
	 		<selectKey keyProperty="eid" order="AFTER" resultType="Integer">
	 			select EMPLOYEES_SEQ.currval from dual
	 		</selectKey>
	 		<!-- 插入时的主键时从序列中拿到的 -->
	 		insert into emp (eid,ename,email,gender) values(employees_seq.nextval,#{ename},#{email},#{gender})
	 </insert>

映射文件的参数问题

	<!-- 
	mybatis的参数问题:
	单个参数:mybatis不会做特殊的处理
			#{参数名/任意的名称} :都可以取出参数的值
			
	多个参数:mybatis会做特殊的处理
			多个参数会被封装成一个map对象,
					key:名称就是param1,param2...paramN,或者参数的索引也可以取出值
					value:就是你传入的参数的值
			
			如果直接写参数名,
			select eid,ename,email,gender from emp where eid= #{eid} and ename=#{ename}
			会报错:
			Cause: org.apache.ibatis.binding.BindingException: 
			Parameter 'eid' not found. 
			Available parameters are [0, 1, param1, param2]
			我们可以将参数写成0,1或者param1,param2
			但是如果参数很多,就显得不合规矩
			所以我们可以命名参数:
				明确指定封装参数时map的key的值是什么,
				使用的是@Param注释进行指明
						key:就是使用@Param的值
						value:参数值
				例如:public Emp getEmpByIdAndName(@Param("eid")Integer eid,@Param("ename")String ename);
	
	上面的命名参数,如果参数实在是不少,那么写起来还是挺麻烦的,下面还有三种方法
	POJO:
		如果多个参数正好是我们业务逻辑的数据模型,那么我们就可以直接传入pojo,就是JavaBean类
		使用:#{属性名}进行取出传入的pojo的属性值
	Map:
		如果多个参数不是业务逻辑的数据模型,没有对应的pojo,不经常使用这个sql,为了方便
		我们可以自己构建一个map传进去
		使用:#{key}就可以取出map对应的值
	TO:
		如果多个参数不是业务模型的数据,但是经常使用,推荐自己编写一个TO
		(Transfer Object)数据传输对象
		
	例子:
			public Emp getEmp(@Param("eid")Integer eid,String ename)
			取值:eid==>#{eid}或#{param1}
				ename==>#{param2}
				
			public Emp getEmp(Integer eid,@Param("e")Emp emp)
			取值:eid==>#{param1}
				ename==>#{param2.ename}或者#{e.ename}
			
			##注意的是:如果是Collection(List,Set)类型或者是数组
			mybatis也会特殊处理,把传入的list或者数组封装在map中
				key:Collection封装成collection
					如果是List可以使用list
					数组封装成array
			
			public Emp getEmpById(List<Integer> eids)
			取值:取出第一个id的值:#{list[0]}
			
	结合源码进行理解:
	参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key
	使用#{key}就可以取到map中的值
	
	例子:
	(@Param("eid")Integer eid,@Param("ename")String ename);
	ParamNameResolver解析参数封装map的;
	1. 首先names:{0=eid,1=ename};这是在构造器的时候就创建好了
		怎么创建的呢:
			1.获取每一个标注了@Param注解的参数的@Param的值eid,ename,赋值给names
			2.每一次解析一个参数给map中保存信息
			(key:参数索引,就是第几个参数,value:name的值
					name的值:标注了@Param注解的:就是注解的值
							 没有标注的:
							 			1.全局配置:isUseActualParamName	(jdk1.8):name=参数名
							 			2.name=map.size();就是当前元素的索引值
							 			比如{0=eid,1=ename,2=2}//如果有第三个元素的话
	2.后面就是对参数args的处理
		假设现在args=[7,"jane"]
		1.如果参数时null就直接返回
		2.如果只有一个元素,并且没有@Param注解,直接返回args[0]
		3.有多个元素或者有@Param注解
			遍历names集合{0=eid,1=ename,2=2}
			names集合的value作为key,names集合的key又作为取值的参数args[entry.getKey()
			由上面的例子得到:
							{eid=args[0],ename=args[1],2=args[2]}
					 结果是:{eid=7,ename="jane",2=2}
		4.最后还额外将每一个参数保存在map中,使用新的key:param1,param2...paramN
			这样就可以:有param注解的可以#{key}取值,或者#{param1}取值
	
	public Object getNamedParams(Object[] args) 
	{
	    final int paramCount = names.size();
	    if (args == null || paramCount == 0) 
	    {
	      return null;
	    }
	    else if (!hasParamAnnotation && paramCount == 1) 
	    {
	      return args[names.firstKey()];
	    } 
	    else
	    {
	      final Map<String, Object> param = new ParamMap<Object>();
	      int i = 0;
	      for (Map.Entry<Integer, String> entry : names.entrySet()) 
	      {
	        param.put(entry.getValue(), args[entry.getKey()]);
	        // add generic param names (param1, param2, ...)
	        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
	        // ensure not to overwrite parameter named with @Param
	        if (!names.containsValue(genericParamName)) 
	        {
	          param.put(genericParamName, args[entry.getKey()]);
	        }
	        i++;
	      }
	      return param;
    }
  
  //names的源码  
  private final SortedMap<Integer, String> names;

  private boolean hasParamAnnotation;

  public ParamNameResolver(Configuration config, Method method) 
  {
    final Class<?>[] paramTypes = method.getParameterTypes();
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) 
    {
      if (isSpecialParameter(paramTypes[paramIndex])) 
      {
        // skip special parameters
        continue;
      }
      String name = null;
      for (Annotation annotation : paramAnnotations[paramIndex]) 
      {
        if (annotation instanceof Param) 
        {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) 
      {
        // @Param was not specified.
        if (config.isUseActualParamName()) 
        {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) 
        {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }
	
	 -->

package bean;

import org.apache.ibatis.annotations.Param;

public interface EmpMapper
{
	public Emp getEmpByIdAndName(@Param("eid")Integer eid,@Param("ename")String ename);
	public Emp getEmpByEmp(Emp emp);
	public Emp getEmpById(Integer id);
	public long addEmp(Emp emp);
	public boolean updateEmp(Emp emp);
	public long deleteEmpById(Integer eid);
}

参数值的获取

	 <!-- 
	 	参数值的获取:
	 		#{}:可以获取map中的值或者pojo对象的属性的值
	 		${};效果和上面的一样
	 	区别是:
	 		#{}是以预编译的形式将参数设置到sql语句中的,使用的方法是PreparedStatement,可以防止sql注入
	 		${}是将取出的值直接拼接在sql语句中的,会有安全问题
	 			大多数情况,我们使用的是#{}
	 			但是原生JDBC不支持占位符的地方,我们就可以使用${}进行取值
	 			比如:分表,排序,模糊查询....
	 				按照年份查询薪资
	 				select * from ${year}_salary where xxxx;
	 				select * from emp orde by ${f_name} ${order}
	 				select * from emp where ename like '%${thechar}%'
	  -->

#{}的其他用法

	  <!-- 
	  	#{}的其他用法:
	  		规定参数的一些规则:
	  			javaType、 jdbcType、 mode(存储过程)、 numericScale、
				resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);
				
				jdbcType通常在某种特定的条件下需要设置:
						就是我们需要插入的数据的某个字段是null值的时候
						有些数据库可能不能识别mybatis对null值得默认处理,
						比如Oracle就无法识别,插入的时候会报错,JdbcType OTHER:无效的类型;
						因为mybatis得全局配置中,对于所有的null值都是默认映射原生得jdbc得OTHER类型
						Oracle无法识别这种类型
				解决的方法有:
						原因是全局配置的jdbcTypeForNull=OTHER,
						1.#{email,jdbcType=OTHER}
						2.将全局配置改变:
								<setting name="jdbcTypeForNull" value="NULL"/>
	   -->
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReflectMirroring

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值