java架构之路-(mybatis源码)mybatis基本使用
我们今天先来简单了解一下我们持久层框架,mybatis的使用。而且现在的注解成为趋势,我主要说一下注解方向的使用吧(配置文件也会说)
从使用角度只要是三个部分,mybatis-config.xml,mapper.xml,执行文件三个部分。
mybatis-config.xml:
主键标签为configuration成对出现的,然后是properties也就是我们的配置,用于配置数据库。settings声明一些配置,比如打印sql语句等,后面会一个个去说。然后就是我们的mappers,里面包含多个mapper标签,也就是对应我们的mapper.xml文件,在这里说一下一共有三种注入的方式,resource,class,url,resource是通过classpath配置的,如果你没有把mapper放置在resources配置下面,需要在maven里设置编译,不然我们的mapper.xml不会编译到classpath里,class通过类来注入mapper,url一般是远程注入的。再就是我们的typehandlers,可以指定类型转换的。我们也可以继承BaseTypeHandler来重写父类的方法来自定义类型转换。
来一个我自己的简单配置。
<?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> <properties resource="app.properties"> <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/> </properties> <settings> <!-- 打印查询语句 --> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <environments default="${default.environment}"> <environment id="dev"> <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> <mappers> <mapper resource="mybatis/dao/StudentMapper.xml"/> <!-- <mapper class="com.tuling.mybatis.dao.UserMapper"></mapper>--> <!-- <mapper url="mybatis.dao.StudentDao"></mapper>--> </mappers> </configuration>
配置还要很多,后面源码解析里面会一点点来说明。
mapper.xml:
这个文件就是我们的编写sql的文件了,里面主要标签就是select,insert,update,delete我们的增删改查标签,再就是我们的缓存设置(二级缓存)。下次博客主要说博客,源码级的。
select里包含我们常见的id,resultType,resultMap,id用来指向我们的接口文件的类名,resultType为我们mybatis自带的类型,也可以是我们设置的对象Bean,resultMap是我们自己定义的返回类型。这里可能会有疑问,一堆多应该怎么来配置?
association我们可以用他来指定一对多的配置,同时可以配置懒查询还是及时查询的。我们来看一个实例,我们现有学生表格分数表,学生对应很多科目的分数,我们来看一下。先来一段测试代码。
<?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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost/jdbc"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <!-- <mapper resource="mybatis/dao/StudentMapper.xml"/>--> <mapper class="mybatis.dao.StudentMapper"></mapper> <mapper class="mybatis.dao.ScoreMapper"></mapper> </mappers> </configuration>
<?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 namespace="mybatis.dao.StudentMapper"> <select id="selectUser" resultType="mybatis.bean.StudentBean"> select * from stu where id = #{id} </select> </mapper>
package mybatis.bean; import java.io.Serializable; public class StudentBean implements Serializable { private static final long serialVersionUID = 2193971369901198487L; private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String toString() { return "selectUser{" + "id=" + id + "name=" + name + '}'; } }
package mybatis; import mybatis.bean.ScoreBean; import mybatis.bean.StudentBean; import mybatis.dao.ScoreMapper; import mybatis.dao.StudentMapper; 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 org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class Test1 { @Test public void test() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); StudentMapper mapper = session.getMapper(StudentMapper.class); StudentBean result = mapper.selectUser(1); System.out.println(result); } }
这样我们查询到我们的学生信息,但是还未包含我们的分数。我们来改造一下。
<?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 namespace="mybatis.dao.StudentMapper"> <resultMap id="studentMap" type="mybatis.bean.StudentBean"> <id property="id" column="id"></id> <collection property="scoreBean" ofType="mybatis.bean.ScoreBean"> <id property="id" column="sid"></id> <!-- <result property="subject" column="subject"></result>--> <result property="score" column="score"></result> <result property="studentId" column="studentId"></result> </collection> </resultMap> <select id="selectUser" resultMap="studentMap"> select t.id,t.name,t.address,t.num,s.id as sid,s.subject,s.score,s.studentId as studentId from student t left join score s on s.studentId = t.id where t.id = #{id} </select> </mapper>
这样就可以查询到对应关系了。需要注意的事子表如果和主表重名,一定给子表起一个别名,而且子表的每一项需要写result,不然没有结果的,但是还不是很好,本来是一个对象一个集合,现在直接变成集合了,我们再来改改。
<?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 namespace="mybatis.dao.StudentMapper"> <resultMap id="studentMap" type="mybatis.bean.StudentBean"> <id property="id" column="id"></id> <collection property="scoreBean" javaType="java.util.ArrayList" ofType="mybatis.bean.ScoreBean" select="mybatis.dao.ScoreMapper.selectScoreByStudentId" column="{studentId=id}"> </collection> </resultMap> <select id="selectUser" resultMap="studentMap"> select * from student t where t.id = #{id} </select> </mapper>
这个比较好用,但是切记,不是懒加载,不是懒加载,需要注意的是collection的property指定我们实体类Bean类中集合的名字。ofType是指向我们一对多中多方的实体Bean,select指定我们对应的第二句sql语句,也就是我们的子查询语句。
column="{studentId=id}" 中studentId是我们的子查询参数名,id是和我们主表的对应关系。就是说,我们要子表的什么参数等于我们的主表的哪个参数传递过去。
接下来就是我们简单的一对一了(也可以当做一对多,但是没啥卵用的多对一,项目经理让从多往一查的时候,请你吐他。。。)我们来看我一下我的配置
<?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 namespace="mybatis.dao.ScoreMapper"> <resultMap id="scoreMap" type="mybatis.bean.ScoreBean"> <id property="id" column="id"></id> <result property="subject" column="subject"></result> <result property="score" column="score"></result> <association property="studentBean" javaType="mybatis.bean.StudentBean" column="studentId"> <id property="id" column="id"></id> <result property="name" column="name"></result> <result property="address" column="address"></result> <result property="num" column="num"></result> </association> </resultMap> <select id="selectScoreByStudentId" resultMap="scoreMap"> select * from score s left join student t on s.studentId = t.id where studentId = #{studentId} </select> </mapper>
这个比较简单,就不说了,就是写了一个resultMap。我们来看一下两条sql的怎么来进行。
<?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 namespace="mybatis.dao.ScoreMapper"> <resultMap id="scoreMap" type="mybatis.bean.ScoreBean"> <id property="id" column="id"></id> <result property="subject" column="subject"></result> <result property="score" column="score"></result> <association property="studentBean" javaType="mybatis.bean.StudentBean" select="mybatis.dao.StudentMapper.selectUser" column="studentId"> <id property="id" column="id"></id> <result property="name" column="name"></result> <result property="address" column="address"></result> <result property="num" column="num"></result> </association> </resultMap> <select id="selectScoreByStudentId" resultMap="scoreMap"> select * from score where studentId = #{studentId} </select> </mapper>
简单解释一下,其实和上面collection差不多的,select指定查询sql位置,column执行传递过去的参数。
其余的insert,update,delete都差不多,我这里就放在一起说了。
id | 命名空间中的唯一标识符,可被用来代表这条语句。 |
parameterType | 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset)。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys | (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 |
keyProperty | (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认值:未设置(unset)。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
keyColumn | (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望使用多个生成的列,也可以设置为逗号分隔的属性名称列表。 |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
useGeneratedKeys=”true”这个相当来说配置的还是比较多的,也就是我们新增成功后,我们的对象可以返回我们插入成功的主键ID。
拼装sql:
#{}:是预处理,也是一个占位符的方式来执行sql,${}是sql的拼接,我们其实可以这样来写。
@Select("select * from ${}_sys_log where id=#{condition}") public SystemLog findSystemLog(String year,String condition);
也就是说,我们对日志sys_log表做了分库分表,按照年份来区分的表,这时我们可以采用sql拼接的方式来做。
但尽力不要用拼接的方式来做,后面我将动态sql会说具体怎么来实现。${}容易被sql注入。所以我们尽力还用占位符的方式来处理我们的SQL。
然后就是我们的插件集成,还有缓存,下次博客我们来说说缓存吧。
忘记了,学mybatis的使用,推荐一个网站https://mybatis.org/mybatis-3/zh/index.html 上面挺全的(没有源码解析,源码还得回来看我博客)。
最进弄了一个公众号,小菜技术,欢迎大家的加入