一、Mybatis的配置
-
新建一个Maven项目,在Maven的配置文件pom.xml中添加Mybatis依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.daitools</groupId> <artifactId>Mybatis</artifactId> <version>1.0-SNAPSHOT</version> <!--mybatis依赖--> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!--mysql依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--lombok依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency> <!--junit依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <!--设置静态资源过滤规则--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project>
-
在项目src>main下创建资源文件夹,命名为
resources
,在该文件夹中创建mybatis的主配置文件,自定义命名,xml格式,配置mybatis<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--设置mybatis输出日志--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <environments default="ssm"> <!--一个数据库连接环境--> <environment id="ssm"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--表示使用的数据源为连接池--> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/ssm"/> <property name="username" value="root"/> <property name="password" value="082800zjh"/> </dataSource> </environment> </environments> <mappers> <!--sql映射文件的位置--> <mapper resource="cn/daitools/Dao/BookMapper.xml"/> </mappers> </configuration>
二、简单使用Mybatis
-
创建一个对象类Student
@Data @AllArgsConstructor @NoArgsConstructor public class Books { private String bookName; private int bookID; private int bookCounts; private String detail; }
-
创建Dao类 BookMapper.java,该类为接口,用于处理对数据的操作
public interface BookMapper { //查询表中的所有数据 public List<Books> selectBooks(); //向表中插入数据 public int insertBook(); //修改一条数据 public int updateBook(); //删除数据库中的数据 public int deleteBook(); }
-
在Dao类的同级目录下创建同名xml文件,作为sql映射文件,即StudentDao.xml,里面配置SQL语句的映射信息
<?xml version="1.0" encoding="UTF-8" ?> <!--Sql语句的映射文件,是用来写sql语句的--> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--命名空间,Mapper类接口的全路径--> <mapper namespace="cn.daitools.Dao.BookMapper"> <!--实体类对象的全路径--> <select id="selectBooks" resultType="cn.daitools.jopo.Books"> select * from books </select> </mapper>
-
创建测试类MyApp.java
public class MyTest { @Test public void test1() throws IOException { String config = "Mybatis.xml"; InputStream in = Resources.getResourceAsStream(config); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); SqlSession sqlSession = factory.openSession(); String sql = "cn.daitools.Dao.BookMapper.selectBooks"; List<Books> list = sqlSession.selectList(sql); for (Books book : list) { System.out.println(book); } sqlSession.close(); } }
三、配置mybatiUtils
-
在dao目录的同级下创建utils工具包,在包内创建mybatiUtils.java用于写工具包代码
private static SqlSessionFactory factory = null; //Mybatis工具类 //定义一个静态代码块,用于创建SqlsessionFactory对象 static { String config = "mybatis.xml"; try { InputStream in = Resources.getResourceAsStream(config); factory = new SqlSessionFactoryBuilder().build(in); } catch (IOException e) { e.printStackTrace(); } } //定义一个静态方法用于获取并返回SqlSession对象 public static SqlSession getSqlSession() { SqlSession sqlSession = null; if (factory != null) { sqlSession = factory.openSession(); } return sqlSession; }
-
在Test.java中使用工具包操作数据
SqlSession sqlSession = getSqlSession(); //获取sqlsession String sql = "org.mybatis.example.BlogMapper.selectStudent"; List<Student> studentList = sqlSession.selectList(sql); for(Student stu:studentList){ System.out.println(stu); } sqlSession.close();
-
用动态代理的方法进行操纵数据(前提是mybatis中的命名空间名字为dao类的全名)
SqlSession sqlSession = MybatisUtils.getSqlSession(); StudentDao dao = sqlSession.getMapper(StudentDao.class); List<Student> list = dao.selectStudent(); for(Student stu:list){ System.out.println(stu); }
四、传递参数
-
传递一个参数
dao.xml中配置使用#{任意命名},传递一个参数即可
-
传递多个参数(命名方式)
在dao.java中使用(@Param(“name”) String name)进行规定传入的参数值与Mybatis中的值相对应
public List<Student> OneParam(@Param("name") String name,@Param("age") Integer age); //注意:Param中的命名须于dao.xml中的#{}中的命名一致 select * from student where name = #{name} or age = #{age}
-
传递多个参数(对象方式)
在dao.java中规定传入的参数类型,可以自定义,这里无限制
public List<Student> OneObject(Student student); //注意:在dao.xml文件中可以直接使用传入对象的参数 select * from student where name = #{name} or age = #{age}
-
传递多个参数(位置方式)
在dao.java中传递多个参数,在dao.xml中使用时,用#{0}、#{1}代替
缺点:不能直观明了的看出传入的值
-
传递多个参数(Map方式)
dao.java中传递多个参数,在dao.xml中使用时,用#{key}代替
缺点:修改时需要改变的地方太多,对Map中的属性不好把握
五、Properties优化
-
第一步 :在资源目录下新建一个db.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8 username=root password=123456
-
第二步 : 将文件导入properties 配置文件
<configuration> <!--导入properties文件--> <properties resource="db.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>
六、typeAliases优化
型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<!--配置别名,注意顺序-->
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
当这样配置时,User
可以用在任何使用com.kuang.pojo.User
的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
每一个在包 com.kuang.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
若有注解,则别名为其注解值。见下面的例子:
@Alias("user")
public class User {
...
}
七、一个完整的settings
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="30"/>
<setting name="defaultFetchSize" value="200"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
八、几个知识点
类型处理器
- 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
- 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】
对象工厂
- MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
- 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。
- 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】
作用域(Scope)和生命周期
作用域理解
- SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
- SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
- 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。
- 因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。
- 如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。
- 所以 SqlSession 的最佳的作用域是请求或方法作用域。
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
- 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。
- JDBC就是一种持久化机制。文件IO也是一种持久化机制。
- 在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。
为什么需要持久化服务呢?那是由于内存本身的缺陷引起的
- 内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。
- 内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。
持久层
什么是持久层?
- 完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】
- 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。
- 不过这里有一个字需要特别强调,也就是所谓的“层”。对于应用系统而言,数据持久功能大多是必不可少的组成部分。也就是说,我们的系统中,已经天然的具备了“持久层”概念?也许是,但也许实际情况并非如此。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现.
- 与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。【说白了就是用来操作数据库存在的!】
九、两种映射方式
-
当数据库中的属性名和实体类的属性名不一致时就需要用到映射来解决
-
手动映射:给数据库的属性名起别名
<select id="selectUserById" resultType="User"> select id , name , pwd as password from user where id = #{id} </select>
-
自动映射:使用结果集映射ResultMap
<resultMap id="UserMap" type="User"> <!-- id为主键 --> <id column="id" property="id"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap> <select id="selectUserById" resultMap="UserMap"> select id , name , pwd from user where id = #{id} </select>
-
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBCResultSets
数据提取代码中解放出来。 -
实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份
resultMap
能够代替实现同等功能的长达数千行的代码。 -
ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。
十、两种分页方式
-
在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。
-
使用limit实现分页
#语法 SELECT * FROM table LIMIT stratIndex,pageSize #StartIndex是查询开始的位置,PageSize是每页显示的数据数量 SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15 #为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1: SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last. #如果只给定一个参数,它表示返回最大的记录行数目: SELECT * FROM table LIMIT 5; //检索前 5 个记录行 #换句话说,LIMIT n 等价于 LIMIT 0,n。
-
使用Rowbounds分页(面向java对象进行的分页,只作为了解内容,实际运用中不使用这个)
@Test //测试使用RowsBounds进行分页查询 public void test6() { RowBounds rb = new RowBounds(1, 2); SqlSession ss = new MybatisUtiis().getSqlSession(); List<Books> bookList = ss.selectList("cn.daitools.Dao.BookMapper.selectBooks", null, rb); for (Books book : bookList) { System.out.println(book); } ss.close(); }
十一、一对多、多对一
-
一对多:两种方式查询
<!--第一种方式查询所有学生和老师的信息--> <select id="getST1" resultMap="ST1"> select * from stu </select> <resultMap id="ST1" type="Student"> <result property="id" column="id"/> <result property="name" column="name"/> <!--配置复杂的数据类型--> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="Teacher"> select * from teacher where id = #{tid} </select> <!--第二种方式查询所有学生和老师的信息--> <select id="getST2" resultMap="ST2"> select s.name sname,s.id sid,t.name tname,t.id tid from stu s,teacher t where t.id = s.tid </select> <resultMap id="ST2" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" column="tid" javaType="Teacher"> <result property="name" column="tname"/> <result property="id" column="tid"/> </association> </resultMap>
-
多对一:两种方式查询
<!--按照结果嵌套查询--> <select id="getStu1" resultMap="getST1"> select s.id sid,t.name tname,t.id tid,s.name sname from teacher t,stu s where t.id = s.tid and t.id = #{tid} </select> <resultMap id="getST1" type="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="tid" column="tid"/> <result property="name" column="sname"/> </collection> </resultMap> <!--按照查询嵌套查询--> <select id="getStu2" resultMap="getST2"> select * from teacher where id = #{tid} </select> <resultMap id="getST2" type="Teacher"> <collection property="students" javaType="ArrayList" ofType="Student" select="getStu" column="id"/> </resultMap> <select id="getStu" resultType="Student"> select * from stu where tid = #{tid} </select>