1.动态代理存在的意义
在三层架构中,业务逻辑层要通过接口访问数据访问层的功能.动态代理可以实现.
2.动态代理的规范
1.UsersMapper.xml文件与UsersMapper.java的接口必须同一个目录下.
2.UsersMapper.xml文件与UsersMapper.java的接口的文件名必须一致,后缀不管.
3.UserMapper.xml文件中标签的id值与与UserMapper.java的接口中方法的名称完全一致.
UserMapper接口
package com.xin.mapper;
import com.xin.pojo.Users;
import java.util.List;
/**
* 数据访问层的接口,规定数据库中可进行的各种操作
*/
public interface UsersMapper {
//查询全部用户信息
List<Users> getAll();
//根据用户主键查用户
Users getById(Integer id);
//根据用户名模糊查询用户
List<Users> getByName(String name);
//用户更新
int update(Users users);
//增加用户
int insert(Users users);
//根据主键删除用户
int delete(Integer id);
}
UserMapper.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="com.xin.mapper.UsersMapper">
<!--
//查询全部用户信息
List<Users> getAll();
-->
<select id="getAll" resultType="users">
select id,username,birthday,sex,address
from users
</select>
<!--
//根据用户主键查用户
Users getById(Integer id);
-->
<select id="getById" parameterType="int" resultType="users">
select id,username,birthday,sex,address
from users
where id=#{id}
</select>
<!--
//用户更新
int update(Users users);
private Integer id;
private String userName;
private Date birthday;
private String sex;
private String address;
-->
<update id="update" parameterType="users">
update users set username=#{userName},birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>
<!--
//根据用户名模糊查询用户
List<Users> getByName(String name);
-->
<select id="getByName" parameterType="string" resultType="users">
select id,username,birthday,sex,address
from users
where username like '%${name}%'
</select>
<!--
//增加用户
int insert(Users users);
-->
<insert id="insert" parameterType="users">
insert into users(username,birthday,sex,address) values(#{userName},#{birthday},#{sex},#{address})
</insert>
<!--
//根据主键删除用户
int delete(Integer id);
-->
<delete id="delete" parameterType="int">
delete from users
where id=#{id}
</delete>
</mapper>
4.UserMapper.xml文件中标签的parameterType属性值与与UserMapper.java的接口中方法的参数类型完全一致.
5.UserMapper.xml文件中标签的resultType值与与UserMapper.java的接口中方法的返回值类型完全一致.
6.UserMapper.xml文件中namespace属性必须是接口的完全限定名称com.xin.mapper.UsersMapper
7.在SqlMapConfig.xml文件中注册mapper文件时,使用class=接口的完全限定名称com.xin.mapper.UsersMapper.
3.动态代理访问的步骤
1)建表Users
2)新建maven工程,刷新可视化
3)修改目录
4)修改pom.xml文件,添加依赖
<!--添加mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<!--添加MyBatis框架依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
</dependencies>
<!--添加指定文件的指定(不指定,所有非java文件不会copy到target里)-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
5)添加jdbc.propertis文件到resources目录下
jdbc.propertis文件
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
6)添加SqlMapConfig.xml文件
SqlMapConfig.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>
<!--读取jdbc.properties属性文件-->
<properties resource="jdbc.properties"></properties>
<!--设置日志输出-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--注册实体类别名-->
<typeAliases>
<package name="com.xin.pojo"/>
</typeAliases>
<!--配置环境变量-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<!--private String driver;
private String url;
private String username;
private String password;
-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--注册mapper.xml文件-->
<mappers>
<!-- 单个实体类注册别名
<mapper class="com.xin.mapper.UsersMapper"></mapper><!–填写的是接口的引用路径–>
-->
<!--批量注册别名-->
<package name="com.xin.mapper"></package>
</mappers>
</configuration>
7)添加实体类
package com.xin.pojo;
import java.util.Date;
public class Users {
private Integer id;
private String userName;
private Date birthday;
private String sex;
private String address;
public Users() {
}
public Users(Integer id, String userName, Date birthday, String sex, String address) {
this.id = id;
this.userName = userName;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
public Users(String userName, Date birthday, String sex, String address) {
this.userName = userName;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Users{" +
"id=" + id +
", userName='" + userName + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
8)添加mapper文件夹,新建UsersMapper接口
9)在mapper文件夹下,新建UsersMapper.xml文件,完成增删改查功能
UsersMapper接口
package com.xin.mapper;
import com.xin.pojo.Users;
import java.util.List;
/**
* 数据访问层的接口,规定数据库中可进行的各种操作
*/
public interface UsersMapper {
//查询全部用户信息
List<Users> getAll();
//根据用户主键查用户
Users getById(Integer id);
//根据用户名模糊查询用户
List<Users> getByName(String name);
//用户更新
int update(Users users);
//增加用户
int insert(Users users);
//根据主键删除用户
int delete(Integer id);
}
UsersMapper.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="com.xin.mapper.UsersMapper">
<!--
//查询全部用户信息
List<Users> getAll();
-->
<select id="getAll" resultType="users">
select id,username,birthday,sex,address
from users
</select>
<!--
//根据用户主键查用户
Users getById(Integer id);
-->
<select id="getById" parameterType="int" resultType="users">
select id,username,birthday,sex,address
from users
where id=#{id}
</select>
<!--
//用户更新
int update(Users users);
private Integer id;
private String userName;
private Date birthday;
private String sex;
private String address;
-->
<update id="update" parameterType="users">
update users set username=#{userName},birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>
<!--
//根据用户名模糊查询用户
List<Users> getByName(String name);
-->
<select id="getByName" parameterType="string" resultType="users">
select id,username,birthday,sex,address
from users
where username like '%${name}%'
</select>
<!--
//增加用户
int insert(Users users);
-->
<insert id="insert" parameterType="users">
insert into users(username,birthday,sex,address) values(#{userName},#{birthday},#{sex},#{address})
</insert>
<!--
//根据主键删除用户
int delete(Integer id);
-->
<delete id="delete" parameterType="int">
delete from users
where id=#{id}
</delete>
</mapper>
10)添加测试类,测试功能
package com.xin.test;
import com.xin.mapper.UsersMapper;
import com.xin.pojo.Users;
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;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
public class MyTest {
SqlSession sqlSession;
//动态代理对象
UsersMapper usersMapper;
//日期的格式化
SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd");
@Before
public void openSqlSession() throws IOException {
//使用文件流读取核心配置文件SqlMapConfig.xml
InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(in);
//取出SqlSession对象
sqlSession=sqlSessionFactory.openSession();
//取出动态代理对象,完成接口中方法的调用,实则是调用xml文件中相应标签的功能
usersMapper=sqlSession.getMapper(UsersMapper.class);
}
@After
public void closeSqlSession() {
sqlSession.close();
}
@Test
public void testGetAll() {
/*
//取出动态代理对象,完成接口中方法的调用,实则是调用xml文件中相应标签的功能
UsersMapper usersMapper=sqlSession.getMapper(UsersMapper.class);
System.out.println(usersMapper.getClass());//class com.sun.proxy.$Proxy7
*/
//调用接口中的方法,mybatis框架已经为我们把功能代理出来了
List<Users> list=usersMapper.getAll();
list.forEach(users-> System.out.println(users));
}
@Test
public void testById() {
/*
usersMapper=sqlSession.getMapper(UsersMapper.class);
*/
Users users= usersMapper.getById(2);
System.out.println(users);
}
@Test
public void testUpdate() throws ParseException {
Users users=new Users(7,"Lihua",sf.parse("2000-01-01"),"2","北京");
int num=usersMapper.update(users);
System.out.println(num);
//切记切记切记:手工提交事务
sqlSession.commit();
}
@Test
public void testByName() {
/*
List<Users> list=usersMapper.getByName("'or 1=1 or 1='");//sql注入
*/
List<Users> list=usersMapper.getByName("张");
list.forEach(users-> System.out.println(users));
}
@Test
public void testInsert() throws ParseException {
Users users=new Users("tom",sf.parse("2000-12-24"),"21","河南周口");
int num=usersMapper.insert(users);
System.out.println(num);
sqlSession.commit();
}
@Test
public void testDelete() {
int num=usersMapper.delete(29);
System.out.println(num);
sqlSession.commit();
}
}
4.优化mapper.xml文件注册
<!--注册mapper.xml文件-->
<mappers>
<!--绝对路径注册-->
<mapper url="/"></mapper>
<!--非动态代理方式下的注册-->
<mapper resource="StudentMapper.xml"></mapper>
<!--动态代理方式下的单个mapper.xml文件注册-->
<mapper class="com.bjpowernode.mapper.UsersMapper"></mapper>
<!--批量注册-->
<package name="com.bjpowernode.mapper"></package>
</mappers>
5.#{}占位符
传参大部分使用#{}传参,它的底层使用的是PreparedStatement对象,是安全的数据库访问 ,防止sql注入.
#{}里如何写,看parameterType参数的类型
1)如果parameterType的类型是简单类型(8种基本(封装)+String),则#{}里随便写.
===>入参类型是简单类型select id,username,birthday,sex,address from users where id=#{zar} ===>随便写 </select>
2)parameterType的类型是实体类的类型,则#{}里只能是类中成员变量的名称,而且区分大小写.
<insert id="insert" parameterType="users" > ===>入参是实体类 insert into users (username, birthday, sex, address) values(#{userName},#{birthday},#{sex},#{address}) ==>成员变量名称 </insert>
6. ${}字符串拼接或字符串替换
1)字符串拼接,一般用于模糊查询中.建议少用,因为有sql注入的风险.
也分两种情况,同样的看parameterType的类型
A. 如果parameterType的类型是简单类型,则${}里随便写,但是分版本,如果是3.5.1及以下的版本,只以写value.
<select id="getByName" parameterType="string" resultType="users"> ===>入参是简单类型 select id,username,birthday,sex,address from users where username like '%${zar}%' ===>随便写 </select>
B. 如果parameterType的类型是实体类的类型,则${}里只能是类中成员变量的名称.(现在已经少用)
C:模糊查询
<!--
//根据用户名模糊查询用户
List<Users> getByName(String name);
-->
<select id="getByName" parameterType="string" resultType="users">
select id,username,birthday,sex,address
from users
where username like '%${name}%'
</select>
测试:
@Test
public void testByName() {
List<Users> list=usersMapper.getByName("'or 1=1 or 1='");//sql注入
// List<Users> list=usersMapper.getByName("张");
list.forEach(users-> System.out.println(users));
}
由于输入的是" ‘or 1=1 or 1=’ ",存在SQL注入,所有的信息都被查了出来
D. 优化后的模糊查询(以后都要使用这种方式)
<select id="getByNameGood" parameterType="string" resultType="users">
select id,username,birthday,sex,address
from users
where username like concat('%',#{name},'%')
</select>
测试:
@Test
public void testByNameGood() {
List<Users> list=usersMapper.getByNameGood("'or 1=1 or 1='");
// List<Users> list=usersMapper.getByName("张");
list.forEach(users -> System.out.println(users));
}
where usernamelike concat(‘%’,#{name},‘%’)使用了字符串拼接,防止了SQL注入,查不到任何信息
2)字符串替换用${}
需求:模糊地址或用户名查询
select * from users where username like ‘%小%’;
select * from users where address like ‘%市%’
<!--
//模糊用户名和地址查询
//如果参数超过一个,则parameterType不写
List<Users> getByNameOrAddress(
@Param("columnName") ===>为了在sql语句中使用的名称
String columnName,
@Param("columnValue") ===>为了在sql语句中使用的名称
String columnValue);
-->
<select id="getByNameOrAddress" resultType="users">
select id,username,birthday,sex,address
from users
where ${columnName} like concat('%',#{columnValue},'%') ==>此处使用的是@Param注解里的名称
</select>
测试;
@Test
public void testGetByNameOrAddress() {
List<Users> list=usersMapper.getByNameOrAddress("username","小");
list.forEach(users-> System.out.println(users));
}
@Test
public void testGetByNameOrAddress() {
// List<Users> list=usersMapper.getByNameOrAddress("username","小");
List<Users> list=usersMapper.getByNameOrAddress("address","市");
list.forEach(users-> System.out.println(users));
}
7.返回主键值
<insert id="insert" parameterType="users">
insert into users(username,birthday,sex,address) values(#{userName},#{birthday},#{sex},#{address})
</insert>
测试:
@Test
public void testInsert() throws ParseException {
Users users=new Users("uu",sf.parse("2000-12-24"),"21","河南周口");
int num=usersMapper.insert(users);
System.out.println(num);
System.out.println(users);
sqlSession.commit();
}
可以看到,主键id=null,是因为在创建Users对象时,并没有给id传参,所以为null
在插入语句结束后, 返回自增的主键值到入参的users对象的id属性中.
<insert id="insert" parameterType="users">
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into users(username,birthday,sex,address) values(#{userName},#{birthday},#{sex},#{address})
</insert>
标签的参数详解:
keyProperty: users对象的哪个属性来接返回的主键值
resultType:返回的主键的类型
order=“BEFORE”:在执行inser插入语句执行前,返回主键的值
order=“AFTER”:执行insert插入语句后返回主键的值
测试:
@Test
public void testInsert() throws ParseException {
Users users=new Users("uu",sf.parse("2000-12-24"),"21","河南周口");
int num=usersMapper.insert(users);
System.out.println(num);
System.out.println(users);
sqlSession.commit();
}
8.UUID
这是一个全球唯一随机字符串,由36个字母数字中划线组成.
UUID uuid = UUID.randomUUID();
System.out.println(uuid.toString().replace("-","").substring(20));