技术、组件、框架、系统
技术:解决某一类问题的方法和手段,jdbc技术,jsp技术 组件:应用程序中可以复用的“零件”(封装的类库),分页组件,数据库访问组件 框架:Framework,是构成一组特定软件可复用设计的一组相互协助工作的类,是一个程序的半 成品。生活中的简历模板,盖房子,钢构结构,活动板房。 系统:实现了完整功能的应用程序,淘宝系统,网上交易系统。程序主要处理的就是数据,数据通常会被存储在数据库(MySQL、Oracle、NoSQL)或内存中。
Java和数据库的操作:Sun公司提供的JDBC接口规范API,具体的接口实现由各家数据库厂商实现。
传统JDBC如何操作数据库?
加载驱动 Class.forName("com.mysql.jdbc.Driver"); 获取连接 DriverManager.getConnection(url, username, password); 创建执行对象 conn.prepareStatement("sql"); 进行CRUD操作--->解析结果集 ps.setXXX()/ps.executeUpdate()/ps.executeQuery()/while(rs.next()) rs.getXXX("列名") 释放连接 底层抛出异常,高层抓取异常。 (DAO层抛出异常)从JavaWeb阶段的学习,我们可以发现
DAO模块:
DAO模块中为了要释放连接,普遍都是try-finally代码块;SQL代码与Java代码耦合;SQL的参数设置,preparedStatement.setXxx(),有些繁琐;SQL作为字符串形式写在参数中,可读性差,尤其是当SQL是复杂SQL时,连接查询、子查询等;读取结果集时设置对象参数复杂,如果对象属性(表的字段)有特别多时,逐一setXxx(rs.getXxx())不现实;每次查询都是物理查询,没有缓存的功能综合所述,参数映射、结果集映射、缓存、多表连接查询等等问题如果实现了,那将不简单是技术,而是框架。
Controller模块:
Servlet读取浏览器、表单参数过程复杂;一个Servlet只能处理一个请求/多个urlPatterns,导致Servlet过多;Servlet支持的视图类型:html、jsp;不支持json;…环境配备
IDEA jdk1.8 和 jdk11 对应mybatis懒加载支持是有区别的 maven3.6.1 (配置中央仓库aliyun)数据库脚本
CREATE DATABASE mybatis; USE mybatis; CREATE TABLE account ( aid INT PRIMARY KEY AUTO_INCREMENT, aname VARCHAR(20) NOT NULL, apassword VARCHAR(20) NOT NULL, a_nickname VARCHAR(20) NOT NULL ); INSERT INTO account VALUES(1, 'zhangsan', '123456', '张三'); INSERT INTO account VALUES(2, 'lisi', '123456', '李四');数据库脚本所有的框架开发,遵循以下流程:
1 maven依赖,jar包拿过来
2 编写核心配置文件
3 根据框架的api编码
4 测试
MyBatis最新版本:3.5.5
数据库驱动:5.1.47
Lombok:简化POJO类的开发
Junit:测试Mapper。
在pom.xml中配置
<?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>org.example</groupId> <artifactId>Mybatis02</artifactId> <version>1.0-SNAPSHOT</version> <!--添加项目依赖--> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!--数据库驱动--> <!--驱动的版本最好和你的数据库版本保持一致--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <!--alibaba json工具,他和mybatis没关系--> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <!-- 这次我是放在src/main/java目录下,配置build标签。 --> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build> </project>配置文件 mybatis-config.xml
<?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 配置--> <configuration> <!--环境,很多环境,一个程序可能操作多个数据源(数据库),而且我们开发环境和本地测试环境也不一样--> <!--对于不同数据库的操作我们理解为环境--> <!--default就是程序默认使用的数据源--> <environments default="development"> <!--其中一个数据源--> <environment id="development"> <!--事务管理 jdbc--> <transactionManager type="JDBC"/> <!--数据源配置,type类型 pooled连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <!--编码 时区--> <!--1433是sqlserver的端口号--> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=UTC&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--加载mapper映射文件--> <mappers> <!--mapper文件的全路径,注意使用/分割--> <mapper resource="com/oracle/mapper/AccountMapper.xml"/> </mappers> </configuration>映射文件,目前放在Java下的com.oracle.mapper包下面,可以放在resource下:
AccountMapper.xml
<?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 命名空间,程序中通过 sql映射的唯一标识获取 sql,比如当前文件有一个 insert sql标识,但是 其他文件中也有一个insert sql标识,就无法区分了,所以不同的sql功能,操作,使用namespce进行区分--> <!--建议namaspce的值 为 包名.文件名--> <mapper namespace="com.oracle.mapper.AccountMapper"> <!--sql代码--> <!--mybatis中提供了四个最基础的sql标签,增删改查--> <!--id是程序中获取sql的标识,完整的标识是 namespace.sqlID--> <!--resultType 结果返回类型--> <select id="selectAll" resultType="com.oracle.model.Account"> select aid,aname,apass,a_nikename as anikename from account </select> <!--通过id查询,#{} 占位符,程序会自动根据参数的类型,选择是否增加 '' --> <select id="selectByPrimayKey" resultType="com.oracle.model.Account"> select aid,aname,apass,a_nikename as anikename from account where aid = #{aid} </select> <!--下面的insert语句中 #{} 中写的是对象的属性--> <insert id="insert" > insert into account(aname,apass,a_nikename) values (#{aname},#{apass},#{anikename}) </insert> <update id="update"> update account set aname=#{aname}, apass=#{apass}, a_nikename=#{anikename} where aid = #{aid} </update> <delete id="delete"> delete from account where aid = #{aid} </delete> </mapper>配置文件,数据库映射文件
ORM中的O和M
o是java对象,m是配置文件
在com.oracle.model包下编写与数据库表相对应的实体类
Account.java
package com.oracle.model; /** * 实体类,mybatis建议实体类放在model包中 * 实际上你放在entity,pojo等都可以 */ public class Account { //实体类中的属性尽量都使用包装类型,因为包装类型默认值是null private Integer aid; private String aname; private String apass; //有意为之,数据库列a_nikename private String anikename; public Integer getAid() { return aid; } public void setAid(Integer aid) { this.aid = aid; } public String getAname() { return aname; } public void setAname(String aname) { this.aname = aname; } public String getApass() { return apass; } public void setApass(String apass) { this.apass = apass; } public String getAnikename() { return anikename; } public void setAnikename(String anikename) { this.anikename = anikename; } }AccountMapper.xml
<?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 命名空间,程序中通过 sql映射的唯一标识获取 sql,比如当前文件有一个 insert sql标识,但是 其他文件中也有一个insert sql标识,就无法区分了,所以不同的sql功能,操作,使用namespce进行区分--> <!--建议namaspce的值 为 包名.文件名--> <mapper namespace="com.oracle.mapper.AccountMapper"> <!--查询 --> <!--sql代码--> <!--mybatis中提供了四个最基础的sql标签,增删改查--> <!--id是程序中获取sql的标识,完整的标识是 namespace.sqlID--> <!--resultType 结果返回类型--> <select id="selectAll" resultType="com.oracle.model.Account"> select * from account </select> </mapper>先实现查询功能
Account.java实体类
package com.oracle.model; /** * 实体类,mybatis建议实体类放在model包中 * 实际上你放在entity,pojo等都可以 */ public class Account { //实体类中的属性尽量都使用包装类型,因为包装类型默认值是null private Integer aid; private String aname; private String apassword; //有意为之,数据库列a_nikename private String anikename; @Override public String toString() { return "Account{" + "aid=" + aid + ", aname='" + aname + '\'' + ", apassword='" + apassword + '\'' + ", anikename='" + anikename + '\'' + '}'; } public Account(Integer aid, String aname, String apassword, String anikename) { this.aid = aid; this.aname = aname; this.apassword = apassword; this.anikename = anikename; } public Account() { } }结果:
[Account{aid=1, aname='zhangsan',apassword='123456', anikename='null'}, Account{aid=2, aname='lisi', apassword='123456', anikename='null'}]表中的数据
可以看到:
结果:由于Account类的anickname和account表的a_nickname不能匹配,所以不能赋值。 所以查出来的值为nullmaven管理测试类必须先用maven test,在target目录中生成编译类才可以进行测试。
从结果中可以看到,并没有封装与数据库表account中a_nickname字段不对应的anickname属性。
解决办法:用别名,因为框架在查询封装时,使用的也是别名,比如JDBC中的Metadata中的getColumnLabel()。
ResultSetMetaData metaData = resultSet.getMetaData(); String columnLabel = metaData.getColumnLabel(1); //最关键,getColumnLabel 获取字段别名,和属性一致,即可匹配修改sql
<select id="selectAll" resultType="com.oracle.model.Account"> select aid,aname,apassword,a_nickname as anickname from account </select>修改之后的查询结果:
[Account{aid=1, aname='zhangsan', apassword='123456', anikename='张三'}, Account{aid=2, aname='lisi', apassword='123456', anikename='李四'}]我们发现mybatis中sql是编写在mapper.xml配置文件中的,这样做的好处是sql代码和java代码相分离,便于sql的维护,更改sql,也不需要重新编译。
在查询完毕后,mybatis自动的将查询结果和java对象进行了关联,且是集合类型。
AccountMapperTest2.java
package com.oracle.mapper; import com.oracle.model.Account; 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.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class AccountMapperTest2 { SqlSessionFactory factory = null; SqlSession sqlSession = null; @Before public void before() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); factory = new SqlSessionFactoryBuilder().build(in); } @Test public void selectById() { sqlSession = factory.openSession(); Account account = sqlSession.selectOne("com.oracle.mapper.AccountMapper.selectById", 1); System.out.println(account); } @After public void after() { if (sqlSession != null) { sqlSession.close(); } } }结果:
Account{aid=1, aname='zhangsan', apassword='123456', anikename='张三'}AccountMapper.xml
<!--下面的insert语句中 #{} 中写的是对象的属性--> <insert id="insert"> insert into account(aname, apassword, a_nickname) values(#{aname}, #{apassword}, #{anickname}) </insert>AccountMapperTest.java
package com.oracle.mapper; import com.oracle.model.Account; 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; public class AccountMapperTest3 { @Test public void insert() throws IOException { InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); //获取sqlsession工厂 //-->sqlsession管理的工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); //获取sqlsession //-->sqlsessoin是和数据库的一次会话,线程不安全,每一次会话是一个新的对象 SqlSession sqlSession = factory.openSession(); Account a = new Account(); a.setAname("wangwu"); a.setApassword("123456"); a.setAnikename("王五"); a.setAid(4); sqlSession.insert("com.oracle.mapper.AccountMapper.insert",a); //事务提交 关闭连接 sqlSession.commit(); sqlSession.close(); } }AccountMapper.xml
<update id="update"> update account set aname = #{aname}, apassword = #{apassword}, a_nickname = #{anickname} where aid = #{aid} </update>AccountMapperTest.java
@Test public void update() { sqlSession = factory.openSession(); Account account = new Account(); account.setAid(4); account.setAname("zhaoliu"); account.setApassword("uiloahz"); account.setAnickname("赵六"); int update = sqlSession.update("com.oracle.mapper.AccountMappe.update", account); sqlSession.commit(); }AccountMapper.xml
<delete id="delete"> delete from account where aid = #{aid} </delete>AccountMapperTest.java
@Test public void delete() { sqlSession = factory.openSession(); int delete = sqlSession.delete("com.oracle.mapper.AccountMappe.delete", 3); sqlSession.commit(); }将XXXmapper.xml放在resources和DAO接口相同的目录下。
将XXXmapper.xml放在resources目录下就不用在pom.xml中设置build标签。
maven的compile命令默认会把src/main/resources下的所有配置文件以及src/main/java下的所有java文件打包或发布到target\classes下面。