准备Mybatis环境->单表CURD->动态SQL语句拼接->多表查询;
注解配置
框架:是整个或部分应用的可重用设计,是可定制化的应用骨架。它可以帮开发人员简化开发过程,提高开发效率。
简而言之,框架是一个应用系统的半成品,开发人员在框架的基础上,根据业务需求开发功能。即:别人搭台,你唱戏。
框架主要是解决了技术整合问题
一个应用系统,必定要选用大量的技术来完成业务需求,实现功能。这就导致应用系统依赖于具体的技术,一旦技术发生了变化或者出现问题,会对应用系统造成直接的冲击,这是应该避免的。
框架的出现,解决了这个问题:框架是技术的整合。如果使用了框架,在框架基础上进行开发,那么开发人员就可以直接调用框架的API来实现功能,而不需要关注框架的底层具体使用了哪些技术。这就相当于框架“屏蔽”了具体的技术,实现了应用系统功能和技术的解耦。
框架一般处于低层应用平台(如JavaEE)和高层业务逻辑之间
每个框架都是要解决一些具体的问题的,我们可以从JavaEE的三层架构,来说一下常见的框架有哪些。
Mybatis:作用在dao层,负责数据库访问的框架。
它原本是Apache的一个开源项目ibatis,后来迁移到了Google code,并改名为Mybatis;之后又迁移到了github上。
它是一个优秀的Java轻量级dao层框架,对JDBC进行了封装,使开发人员只需要关注SQL语句,而不需要关注JDBC的API执行细节。
SQL:面向关系的
Hibernate:作用在dao层,负责数据库访问的框架。
Hibernate是一个完全面向对象的Dao层框架,封装程度非常高,开发人员可以完全以面向对象的方式操作数据库,甚至不需要编写SQL语句。
但是,正因为Hibernate的封装程度过高,导致它的执行效率受到了影响,是重量级框架。目前在国内使用的越来越少了。
SpringMVC:作用在web层,负责和客户端交互的框架。
SpringMVC是Spring Framework的后续产品,受益于Spring框架的流行,并且因为SpringMVC使用简单、和Spring整合简单,导致SpringMVC框架在国内使用的也越来越多。
Struts1/Struts2:作用在web层,负责和客户端交互的框架。
Struts1是比较老的框架,目前已经基本不使用了。
Struts2目前使用的也越来越少,逐步被SpringMVC代替
Spring:不是作用在某一层,而是实现web层、Service层、Dao层之间解耦的框架,是三层之间的粘合剂
Spring框架是为了解决应用开发的复杂性而创建的,任何Java应用都可以从Spring中受益。Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
什么是框架:是应用系统的半成品,可以开发人员的代码更简单,但是功能更强
框架解决了干什么问题:解决了技术整合的问题
有哪些常用框架:
//注册驱动
//获取连接
//创建statement/预编译对象
//执行SQL语句
//处理结果
//释放资源
Mybatis是一个优秀的Java轻量级持久层框架。
ORM:Object Relational Mapping,对象关系映射思想。指把Java对象和数据库的表和字段进行关联映射,从而达到操作Java对象,就相当于操作了数据库。查询了数据库,自动封装成JavaBean对象
查询所有用户信息,获取用户集合List
因为只涉及Dao层,不涉及客户端,所以只要创建Java项目即可。项目坐标信息如下:
groupId: com.itheima
artifactId: mybatis01_quickstart
version: 1.0-SNAPSHOT
packing: jar
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.46version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.10version>
<scope>providedscope>
dependency>
dependencies>
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//get/set...
//toString...
}
在Mybatis里,把dao层的接口称之为映射器(取 调用接口的方法即相当于操作数据库 之意)。
映射器的类名,可以叫XXXMapper
,也可以叫XXXDao
。我们这里按照之前的习惯,取名UserDao
注意:只要创建接口即可,不需要创建接口的实现类
public interface UserDao {
List<User> queryAll();
}
注意:
映射配置文件名称要和映射器类名一样。例如:映射器叫UserDao,那么配置文件就叫UserDao.xml
映射配置文件位置要和映射器位置一样。例如:映射器在com.itheima.dao里,那么配置文件就应该在resources的com/itheima/dao目录下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJlAxMdY-1585403917876)(img/image-20200227110903440.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S3LgInC4-1585403917876)(img/image-20200227110940352.png)]
<mapper namespace="com.itheima.dao.UserDao">
<select id="queryAll" resultType="com.itheima.domain.User">
select * from user
select>
mapper>
Mybatis支持使用log4j输出执行日志信息,但是需要我们提供log4j的配置文件:log4j.properties
注意:
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
注意:
<configuration>
<environments default="mysql_mybatis">
<environment id="mysql_mybatis">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/itheima/dao/UserDao.xml"/>
mappers>
configuration>
@Test
public void testQuickStart() throws IOException {
//1. 读取核心配置文件SqlMapConfig.xml
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//2. 创建SqlSessionFactoryBuilder构造者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3. 使用构造者builder,根据配置文件的信息is,构造一个SqlSessionFactory工厂对象
SqlSessionFactory factory = builder.build(is);
//4. 使用工厂对象factory,生产一个SqlSession对象
SqlSession session = factory.openSession();
//5. 使用SqlSession对象,获取映射器UserDao接口的代理对象
UserDao dao = session.getMapper(UserDao.class);
//6. 调用UserDao代理对象的方法,查询所有用户
List<User> users = dao.queryAll();
for (User user : users) {
System.out.println(user);
}
//7. 释放资源
session.close();
is.close();
}
准备工作
编写代码,使用Mybatis实现功能
log4j.properties
功能测试
//1. 加载配置文件
InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml");
//2. 获取映射器的代理对象:构造者构造一个工厂,工厂生产一个SqlSession,SqlSession创建代理对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
UserDao dao = session.getMapper(UserDao.class);
List<User> userList = dao.queryAll();
//3. 释放资源
session.close();
is.close();
针对user表进行CURD操作:
List
(上节课快速入门已写过,略)User
<groupId>com.itheimagroupId>
<artifactId>day47_mybatis01_curdartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>jarpackaging>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.46version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//get/set...
//toString...
}
public interface UserDao {
}
<mapper namespace="com.itheima.dao.UserDao">
mapper>
<configuration>
<environments default="mysql_mybatis">
<environment id="mysql_mybatis">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/itheima/dao/UserDao.xml"/>
mappers>
configuration>
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
在单元测试类中准备好@Before、@After的方法代码备用。编写完成一个功能,就增加一个@Test方法即可。
代码如下:
public class MybatisCURDTest {
private InputStream is;
private SqlSession session;
private UserDao dao;
@Before
public void init() throws IOException {
is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
session = factory.openSession();
dao = session.getMapper(UserDao.class);
}
@After
public void destory() throws IOException {
//释放资源
session.close();
is.close();
}
}
void save(User user);
<insert id="save" parameterType="com.itheima.domain.User">
<selectKey resultType="int" keyProperty="id" order="AFTER">
select last_insert_id()
selectKey>
insert into user (id, username, birthday, address, sex)
values (#{id}, #{username}, #{birthday},#{address},#{sex})
insert>
注意:
SQL语句中#{}代表占位符,相当于预编译对象的SQL中的?,具体的值由User类中的属性确定
@Test
public void testSaveUser(){
User user = new User();
user.setUsername("tom");
user.setAddress("广东深圳");
user.setBirthday(new Date());
user.setSex("男");
System.out.println("保存之前:" + user);//保存之前,User的id为空
dao.save(user);
session.commit();//注意:必须要手动提交事务
System.out.println("保存之后:" + user);//保存之后,User的id有值
}
注意:执行了DML语句之后,一定要提交事务:session.commit();
在映射器里增加方法
在映射配置文件里增加statement
<insert id="方法名" parameterType="参数类型,全限定类名">
<selectKey resultType="int" keyProperty="属性名" order="AFTER">
select last_insert_id()
selectKey>
insert语句
insert>
功能测试
session.commit()
void edit(User user);
<update id="edit" parameterType="com.itheima.domain.User">
update user set username = #{username}, birthday = #{birthday},
address = #{address}, sex = #{sex} where id = #{id}
update>
@Test
public void testEditUser(){
User user = new User();
user.setId(50);
user.setUsername("jerry");
user.setAddress("广东深圳宝安");
user.setSex("女");
user.setBirthday(new Date());
dao.edit(user);
session.commit();
}
注意:执行了DML语句之后,一定要提交事务:session.commit();
在映射器里增加方法
在映射配置文件里增加statement
<update id="方法名称" parameterType="参数类型,全限定类名">
update语句
update>
功能测试
session.commit()
void delete(Integer id);
<delete id="delete" parameterType="int">
delete from user where id = #{id}
delete>
注意:
如果只有一个参数,且参数是简单类型的,那么#{id}中的id可以随意写成其它内容,例如:#{abc}
简单类型参数:
int, double, short, boolean, … 或者:java.lang.Integer, java.lang.Double, …
string 或者 java.lang.String
@Test
public void testDeleteUser(){
dao.delete(50);
session.commit();
}
注意:执行了DML语句之后,一定要提交事务:session.commit();
在映射器里增加方法
在映射配置文件里增加statement
<delete id="方法名称" parameterType="参数类型,写全限定类名">
delete from user where id = #{随意写}
delete>
注意:如果SQL语句只有一个参数,并且是简单类型,在#{}
里边可以写任意值
功能测试
session.commit()
User findById(Integer id);
<select id="findById" parameterType="int" resultType="com.itheima.domain.User">
select * from user where id = #{id}
select>
@Test
public void testFindUserById(){
User user = dao.findById(48);
System.out.println(user);
}
/**
* 使用#{}方式进行模糊查询
*/
List<User> findByUsername1(String username);
<select id="findByUsername1" parameterType="string" resultType="com.itheima.domain.User">
select * from user where username like #{username}
select>
注意:只有一个参数,且是简单参数时, #{username}中的username可以写成其它任意名称
/**
* 使用#{}方式进行模糊查询--单元测试方法
*/
@Test
public void testFindUserByUsername1(){
List<User> users = dao.findByUsername1("%王%");
for (User user : users) {
System.out.println(user);
}
}
注意:模糊查询的条件值,前后需要有%
/**
* 使用${value}方式进行模糊查询
*/
List<User> findByUsername2(String username);
<select id="findByUsername2" parameterType="string" resultType="com.itheima.domain.User">
select * from user where username like '%${value}%'
select>
注意:
${value}是固定写法,不能做任何更改
在SQL语句中已经加了%,那么在测试代码中,传递参数值时就不需要再加%了
/**
* 使用${value}方式进行模糊查询--单元测试方法
*/
@Test
public void testFindUserByUsername2(){
List<User> users = dao.findByUsername2("王");
for (User user : users) {
System.out.println(user);
}
}
注意:SQL语句中已经加了%, 参数值前后不需要再加%
#{}
:表示一个占位符,相当于预编译对象的SQL中的?
#{}
写的是属性名称。如果只有一个参数并且是简单类型,里边可以是value或者其它名称映射配置文件中的SQL:select * from user where username like #{username}
单元测试中传递实参:%王%
最终执行的SQL:select * from user where username like ?
参数值:%王%
${}
:表示拼接SQL串,相当于把实际参数值,直接替换掉${}
不能防止SQL注入
Mybatis不进行参数的Java类型和JDBC类型转换
${}
写的是属性名称。如果只有一个参数并且是简单类型,${value}
中只能是value,不能是其它名称
(Mybatis3.4 里存在这种现象,3.5没有了)
映射配置文件中的SQL:select * from user where username like '%${value}%'
单元测试中传递实参:王
最终执行的SQL:select * from user where username like '%王%'
功能实现的步骤:
在映射器里增加方法
在映射配置文件里增加statement
#{}
方式<select id="search1" parameterType="java.lang.String" resultType="com.itheima.domain.User">
select * from user where username like #{username}
select>
${}
方式<select id="search2" parameterType="java.lang.String" resultType="com.itheima.domain.User">
select * from user where username like '%${value}%'
select>
功能测试
#{}
和${}
的区别:
#{}
本质是预编译方式,可以有效防止SQL注入;${}
本质是拼接字符串方式,不能防止SQL注入#{}
会自动转换Java类型和JDBC类型;${}
不会自动转换Java类型和JDBC类型#{}
里边可以写任意值${}
里边只能写${value}
。(Mybatis3.5之前的版本)Integer findTotalCount();
<select id="findTotalCount" resultType="int">
select count(*) from user
select>
@Test
public void testFindTotalCount(){
Integer totalCount = dao.findTotalCount();
System.out.println(totalCount);
}
OGNL:Object Graphic Navigator Language,是一种表达式语言,用来从Java对象中获取某一属性的值。本质上使用的是JavaBean的getXxx()方法。
例如:user.getUsername()—> user.username
例如:user.getAddress().getProvince()—>user.address.province
list.size()>0
, list != null
在#{}
里、${}
可以使用OGNL表达式,从JavaBean中获取指定属性的值
例如:int, double, short 等基本数据类型,或者string
或者:java.lang.Integer, java.lang.Double, java.lang.Short, java.lang.String
SQL语句里获取参数:
如果是一个简单类型参数,写法是:#{随意}
如果parameterType是POJO类型,要写全限定类名
例如:parameterType是com.itheima.domain.User
#{JavaBean的属性名}
在web应用开发中,通常有综合条件的搜索功能,例如:根据商品名称 和 所属分类 同时进行搜索。这时候通常是把搜索条件封装成JavaBean对象;JavaBean中可能还有JavaBean。
#{xxx.xx.xx}
根据用户名搜索用户信息,查询条件放到QueryVO的user属性中。QueryVO如下:
public class QueryVO {
private User user;
//private String className;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
List<User> findByVO(QueryVO vo);
<select id="findByVO" parameterType="com.itheima.domain.QueryVO" resultType="com.itheima.domain.User">
select * from user where username like #{user.username}
select>
@Test
public void testFindByVO(){
QueryVO vo = new QueryVO();
User user = new User();
user.setUsername("%王%");
vo.setUser(user);
List<User> users = dao.findByVO(vo);
for (User user1 : users) {
System.out.println(user1);
}
}
注意:resultType是查询select标签上才有的,用来设置查询的结果集要封装成什么类型的
例如:int, double, short 等基本数据类型,或者string
或者:java.lang.Integer, java.lang.Double, java.lang.Short, java.lang.String
例如:com.itheima.domain.User
注意:JavaBean的属性名要和字段名保持一致
有JavaBean类User2,属性名和数据库表的字段名不同。要求查询user表的所有数据,封装成User2的集合。其中User2如下:
public class User2 {
private Integer userId;
private String username;
private Date userBirthday;
private String userSex;
private String userAddress;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
@Override
public String toString() {
return "User2{" +
"userId=" + userId +
", username='" + username + '\'' +
", userBirthday=" + userBirthday +
", userSex='" + userSex + '\'' +
", userAddress='" + userAddress + '\'' +
'}';
}
}
/**
* JavaBean属性名和字段名不一致的情况处理---方案一
* @return
*/
List<User2> queryAll_plan1();
<select id="queryAll_plan1" resultType="com.itheima.domain.User2">
select id as userId, username as username, birthday as userBirthday, address as userAddress, sex as userSex from user
select>
/**
* JavaBean属性名和字段名不一致的情况处理---方案一 单元测试代码
*/
@Test
public void testQueryAllUser2_plan1(){
List<User2> user2List = dao.queryAll_plan1();
for (User2 user2 : user2List) {
System.out.println(user2);
}
}
/**
* JavaBean属性名和字段名不一致的情况处理--方案二
* @return
*/
List<User2> queryAll_plan2();
<select id="queryAll_plan2" resultMap="user2Map">
select * from user
select>
<resultMap id="user2Map" type="com.itheima.domain.User2">
<id property="userId" column="id"/>
<result property="username" column="username"/>
<result property="userBirthday" column="birthday"/>
<result property="userAddress" column="address"/>
<result property="userSex" column="sex"/>
resultMap>
/**
* JavaBean属性名和字段名不情况处理--方案二 单元测试代码
*/
@Test
public void testQueryAllUser2_plan2(){
List<User2> user2List = dao.queryAll_plan2();
for (User2 user2 : user2List) {
System.out.println(user2);
}
}