mysql
创建数据库
-- 创建数据库: 创建一个名为db1的数据库 create database db1; -- 切换数据库: use db1;
增删改数据
所有字段插入值
insert into tb_emp values (null,'忘了爱','111','杨宁',1,'/img',1,'2002-12-21',now(),now());
批插
insert into tb_emp values (null,'忘了爱1','111','杨宁2',1,'/img',1,'2002-12-21',now(),now()) ,(null,'忘了爱2','111','杨宁3',1,'/img',1,'2002-12-21',now(),now());
修改
update tb_emp set name = '传智',entrydate='2022-01-01' where id =1;
删除
delete from tb_emp where id = 1;
查数据
交叉连接
select * from emp,dept;
内连接
select * from emp e inner join dept d on e.dept_id = d.id;
左外连接
select * from emp e left join dept d on e.dept_id = d.id;
右外连接
select * from emp e right join dept d on e.dept_id = d.id;
子查询
-- 1: 查询工资小于平均工资的员工有哪些?(子查询结果为一个值 标量子查询) select * from emp where salary < (select avg(salary) from emp); -- 2: 查询工资大于5000的员工,所在部门的名字 (子查询结果为多个值 列子查询) select name from dept where id in(select distinct dept_id from emp where salary > 5000); -- 3: 查询出2011年以后入职的员工信息,包括部门信息 (子查询结果为一张表 表子查询) select * from (select * from emp where join_date >= '2011-01-01') as l left join dept as d on l.dept_id = d.id;
复杂查询
# 菜品表:dish、分类表:category、套餐表:setmeal -- 1. 查询价格低于 10元 的菜品的名称 、价格 及其 菜品的分类名称 select d.name,d.price,c.name from dish d inner join category c on d.category_id = c.id where d.price <10; -- 2. 查询所有价格在 10元(含)到50元(含)之间 且 状态为'起售'的菜品名称、价格 及其 菜品的分类名称 # (即使菜品没有分类, 也需要将菜品查询出来). select d.name,d.price,c.name from dish d left join category c on d.category_id = c.id where d.price between 10 and 50 and d.status = 1; -- 3. 查询出 "商务套餐A" 中包含了哪些菜品 (展示出套餐名称、价格, 包含的菜品名称、价格、份数) # 三表关联,重复使用join select s.name,s.price,d.name,d.price,sd.copies from setmeal s join setmeal_dish sd on s.id = sd.setmeal_id join dish d on d.id = sd.dish_id where s.name = '商务套餐A'; -- 4. 查询出低于菜品平均价格的菜品信息 (展示出菜品名称、菜品价格) select name,price from dish where price < (select avg(price) from dish) order by price; -- 5. 查询每个分类下最贵的菜品, 展示出分类的名称、最贵的菜品的价格 select c.name,max(d.price) from dish d join category c on d.category_id = c.id group by c.name; -- 6. 查询各个分类下状态为'起售' , 并且该分类下 菜品总数量大于等于3的分类名称 select c.name,count(*) count from category c join dish d on c.id = d.category_id where c.status = 1 group by c.name having count >=3;
多表查询关键字
-
INNER JOIN: 内连接,返回两个表中匹配的行。
-
LEFT JOIN: 左连接,返回左表中的所有行,以及右表中与左表匹配的行。
-
RIGHT JOIN: 右连接,返回右表中的所有行,以及左表中与右表匹配的行。
-
FULL JOIN: 全连接,返回左表和右表中的所有行,如果没有匹配的行,则以NULL填充。
-
CROSS JOIN: 交叉连接,返回两个表中所有可能的组合。
此外,还有一些其他的关键字和子句可以在多表查询中使用:
-
ON: 在连接时指定条件进行匹配,使用语法如下:
SELECT * FROM table1 JOIN table2 ON table1.column = table2.column
。 -
USING: 在连接时指定某些列进行匹配,使用语法如下:
SELECT * FROM table1 JOIN table2 USING (column)
。 -
WHERE: 在查询中添加条件来过滤结果集。
-
GROUP BY: 按照某个列或者多个列对结果进行分组。
-
HAVING: 在GROUP BY之后使用条件过滤结果。
分组过滤
分组过滤: select 分组字段,聚合函数() from 表名
group by 分组字段名
having 分组后过滤条件
排序
排序: select 字段列表 from 表名 order
by 字段1 排序方式1 , 字段2 排序方式2
ASC:升序(默认值)
DESC:降序
分页
分页: select 字段列表 from 表名
limit 起始索引, 查询记录数
排序: order by 列 desc
截取: limit 开始,几个
聚合函数: count(列)
分组: group by 列
过滤: having 过滤条件
普通条件: where
外键
创建表之后单独添加
# alter table 表名 add [constraint 约束名] foreign key (列名) references 主表(主键) alter table student add constraint class_id_fk foreign key (class_id) references class (id);
创建表的时候,添加外键
create table user_card( id int unsigned primary key auto_increment, nationality varchar(10) not null , birthday date not null , idcard char(18) not null , issued varchar(20) not null , expire_begin date not null , expire_end date, user_id int unsigned comment '用户id', # 创建表的时候,添加外键 (user_id 和 user表的id) constraint userid_id_fk foreign key (user_id) references user(id) );
mybatis
配置文件
<?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> <settings> <!--在控制台输出发送的sql日志--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!--目前只关注这部分内容,它的作用就是声明要连接的数据信息--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="1234"/> </dataSource> </environment> </environments> <mappers> <!--声明含有sql的接口所在包--> <package name="com.itheima.mapper"/> </mappers> </configuration>
Mapper
// 1. 这是一个接口 // 2. 接口名:操作的表名+Mapper public interface UserMapper { // #{}里面的内容 // 1. 如果参数是对象类型,#{}写的是对象的属性名 @Insert("insert into user values (null,#{name},#{age},#{gender},#{phone})") void save(User user); // @Options // useGeneratedKeys 拿到自增主键 // keyProperty 指定哪个字段是主键 @Options(useGeneratedKeys = true, keyProperty = "id") @Insert("insert into user values (null,#{name},#{age},#{gender},#{phone})") void save2(User user); @Update("update user set name = #{name},age=#{age},gender=#{gender},phone = #{phone} where id=#{id} ") void update(User user); @Delete("delete from user where id = #{id}") void delete(Integer id); @Delete("delete from user where name = #{name}") void deleteByName1(String name); @Delete("delete from user where name = ${name}") void deleteByName2(String name); }
测试类
public class UserMapperTest { @Test public void testSave() throws IOException { // 1. 创建对象 User user = new User(); user.setName("张三"); user.setAge(18); user.setGender(1); user.setPhone("15515847153"); // 工具类获取sqlSession SqlSession sqlSession = MyBatisUtil.getSqlSession(); // 获取UserMapper对象,调用方法 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.save(user); // 工具类关闭sqlSession MyBatisUtil.close(sqlSession); } @Test public void testDeleteByName1() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.deleteByName1("张三"); // delete from user where name = '张三' MyBatisUtil.close(sqlSession); } @Test public void testDeleteByName2() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); // 用户名叫作:'李四' or id>1 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.deleteByName2(" '李四' or id>1"); // ${}会产生sql注入问题 // delete from user where name = '李四' or id>1 MyBatisUtil.close(sqlSession); } }
xml写sql
foreach
<!-- * collection:集合名称 * item:集合遍历出来的元素 * separator:每一次遍历使用的分隔符 * open: 遍历开始前拼接的片段 * close:遍历结束后拼接的片段 delete from emp where id in (1,2,3) --> <delete id="deleteByIds"> delete from emp where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete>
sql片段
<!--ctrl + alt + L:格式化代码--> <!-- * 定义SQL片段: <sql id="selectUser"></sql> * 引用SQL片段: <include refid="selectUser"></include> --> <sql id="selectUser"> select id, username, password, name, gender, image, job, entrydate as ed, dept_id, create_time, update_time from emp </sql> <!--id:哪个方法--> <!--resultType:方法返回值类型(sql结果数据类型)--> <select id="findById" resultType="com.itheima.domain.Emp"> <include refid="selectUser"></include> where id = #{id} </select>
查询&修改
<!-- 字符串需要跟null和空串比较 其他类型只要跟null比较 if:使用test进行条件判断,只有条件成立,条件中的sql才会生效 where:只会在<where>标签内部有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR。 --> <select id="finByCondition" resultType="com.itheima.domain.Emp"> <include refid="selectUser"/> <where> <if test="name != null and name !='' "> and name like concat('%',#{name},'%') </if> <if test="gender != null"> and gender = #{gender} </if> <if test="begin != null and end != null"> and entrydate between #{begin} and #{end} </if> </where> </select> <!--set:动态地在set代码块之前加入SET关键字,并删掉set代码块中最后一个多余的逗号(用在update语句中)--> <!--ctrl + shift + / 生成快捷键 --> <update id="update"> update emp <set> <if test="username != null and username != '' "> username = #{username}, </if> <if test="name != null and name != '' "> name = #{name}, </if> <if test="gender != null"> gender = #{gender}, </if> <if test="job != null"> job = #{job}, </if> <if test="ed != null"> entrydate = #{ed}, </if> <if test="deptId != null"> dept_id = #{deptId}, </if> </set> where id = #{id} </update>
驼峰映射
<settings> <!--在控制台输出发送的sql日志--> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--驼峰映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
Mapper接口映射
<mappers> <!--声明含有sql的接口所在包--> <package name="com.itheima.mapper"/> </mappers>
resultMap
<resultMap id="empResultMap" type="com.itheima.domain.Emp"> <id property="id" column="id"></id> <!--设置主键--> <!--result:建立映射关系--> <!--property:实体类属性,column:数据库表字段--> <result property="username" column="username"></result> <result property="password" column="password"></result> <result property="name" column="name"></result> <result property="gender" column="gender"></result> <result property="image" column="image"></result> <result property="job" column="job"></result> <result property="ed" column="entrydate"></result> <result property="deptId" column="dept_id"></result> <result property="createTime" column="create_time"></result> <result property="updateTime" column="update_time"></result> </resultMap> <!--id:哪个方法--> <!--resultType:方法返回值类型(sql结果数据类型)--> <select id="findById" resultType="com.itheima.domain.Emp"> <include refid="selectUser"></include> where id = #{id} </select> <!--resultMap:sql结果通过resultMap映射到实体类字段中--> <select id="selectAll" resultMap="empResultMap"> select * from emp </select>
前端
js
函数定义
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>函数</title> </head> <body> <!--定义一个函数(方法)实现两个数相加--> <script> // 方式一:定义函数(方法) function add(a, b) { return a + b } let result = add(1, 2) console.log(result) // 方式二:定义函数 let add2 = function (a, b) { return a + b } let result2 = add2(1,2); console.log(result2) </script> </body> </html>
事件绑定
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>事件绑定</title> </head> <body> <input type="button" value="按钮1" οnclick="tan()"> <br> <input type="button" value="按钮2" id="btn"> <br> <!-- 事件绑定有两种方式: 1. 通过 HTML标签中的事件属性进行绑定 2. 通过 DOM 元素属性绑定 --> <script> // 1. 通过 HTML标签中的事件属性进行绑定 function tan() { alert('按钮一被点击了') } // 2. 通过 DOM 元素属性绑定 (document代表当前页面对象) document.getElementById('btn').onclick = function () { alert('按钮二被点击了') } </script> </body> </html>
事件绑定综合案例
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>案例:页面交互</title> <!-- 常用事件: 1. onfocus 获取焦点 2. onblur 失去焦点 3. onchange 表单控件的值改变时 4. onclick 鼠标单击 5. onload 页面加载完成 --> <script> //5. 将js代码调整到页面的head部分,使其依旧可以运行 window.onload = function (){ //1. 当姓名框获得焦点之后,设置其value值为传智 document.getElementById('username').onfocus = function () { // document.getElementById('username').value = '传智' this.value = '传智' // this代表当前对象 } //2. 当姓名框失去焦点之后,设置其value值为黑马 document.getElementById('username').onblur = function () { this.value = '黑马' } //3. 当学历发生变化的时候,在控制台打印出变化之后的value值 document.getElementById('edu').onchange = function () { console.log(this.value) } //4. 当鼠标点击按钮时,弹窗显示被点击了 document.getElementById('btn').onclick = function () { alert('被点击了') } } </script> </head> <body> 姓名 <input type="text" id="username"><br/> 学历 <select name="edu" id="edu"> <option value="0">请选择</option> <option value="1">本科</option> <option value="2">大专</option> </select> <br/> <button id="btn">按钮</button> </body> </html>
Servlet
路径
@WebServlet("/login")
req & resp
@WebServlet("/requestServlet") public class RequestServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 请求参数编码,设置为UTF-8 req.setCharacterEncoding("UTF-8"); String name = req.getParameter("name"); String age = req.getParameter("age"); System.out.println("name=" + name + " age=" + age); String[] hobbies = req.getParameterValues("hobby"); System.out.println(Arrays.toString(hobbies)); } }
@WebServlet("/responseServlet") public class ResponseServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 响应成功状态码 resp.setStatus(200); // 设置响应头 resp.setContentType("text/html;charset=utf-8"); // 返回数据 PrintWriter writer = resp.getWriter(); writer.write("hello servlet"); } }
postman
模拟浏览器发起请求
http://localhost:8080/requestServlet?name=张三&age=18
对象转json字符串 & json字符串转对象
<!--json转换工具--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9</version> </dependency>
Student student = new ObjectMapper().readValue(json, Student.class); System.out.println(student);
Student student = studentService.findById(id); String json = new ObjectMapper().writeValueAsString(student);
增删改查案例
@WebServlet("/studentServlet") public class StudentServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter("action"); if ("findAll".equals(action)) { // 1. 操作service层 StudentService studentService = new StudentServiceImpl(); List<Student> students = studentService.findAll(); // 2. 把数据转为json字符串格式 String json = new ObjectMapper().writeValueAsString(students); System.out.println(json); // 3. 返回数据给前端 resp.getWriter().write(json); } else if ("save".equals(action)) { // 1. 以流的方式接收前端json数据 ServletInputStream inputStream = req.getInputStream(); String json = IoUtil.read(inputStream, "utf-8"); // 2. 把json字符串转为对象 Student student = new ObjectMapper().readValue(json, Student.class); System.out.println(student); // 3. 调用service层,添加学生 StudentService studentService = new StudentServiceImpl(); studentService.save(student); // 4. 返回ok resp.getWriter().write("OK"); } else if ("deleteById".equals(action)) { String id = req.getParameter("id"); StudentService studentService = new StudentServiceImpl(); studentService.deleteById(id); resp.getWriter().write("OK"); } else if ("findById".equals(action)) { String id = req.getParameter("id"); StudentService studentService = new StudentServiceImpl(); Student student = studentService.findById(id); String json = new ObjectMapper().writeValueAsString(student); resp.getWriter().write(json); } else if ("update".equals(action)) { ServletInputStream inputStream = req.getInputStream(); String json = IoUtil.read(inputStream, "utf-8"); Student student = new ObjectMapper().readValue(json, Student.class); System.out.println(student); StudentService studentService = new StudentServiceImpl(); studentService.update(student); resp.getWriter().write("OK"); } } }
Spring
Spring整合Mybatis
依赖
<dependencies> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.26</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <!--spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.16</version> </dependency> <!--spring-jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.16</version> </dependency> <!--spring-mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.7</version> </dependency> <!--测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.16</version> </dependency> </dependencies>
配置类
@ComponentScan("com.itheima") // @ComponentScan 注解扫描范围 public class SpringConfig { // 1. 配置数据库连接池对象 @Bean public DruidDataSource dataSource(){ // 创建DruidDataSource对象 DruidDataSource dataSource = new DruidDataSource(); // 设置连接数据库的参数:driver(加载器)、路径、用户名、密码 dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/spring"); dataSource.setUsername("root"); dataSource.setPassword("1234"); return dataSource; } // 2. 配置mapper映射对象 @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer configurer = new MapperScannerConfigurer(); configurer.setBasePackage("com.itheima.mapper"); // 设置接口所在包名 return configurer; } // 3. 配置SqlSessionFactoryBean // @Bean标注的方法如果需要参数,Spring会在自己的容器中查找,自动注入 @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DruidDataSource dataSource){ SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); return factoryBean; } }
Spring写测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class DeptMapperTest { @Autowired private DeptService deptService; @Test public void testFindAll() { // 3. 验证deptService是否拿到;验证deptService是否拿到DeptMapper对象 List<Dept> deptList = deptService.findAll(); deptList.forEach(dept -> System.out.println(dept)); } @Test public void deleteByIdTest() { deptService.deleteById(1); } @Test public void insertDept() { Dept dept = new Dept(); dept.setId(6); dept.setName("网管部"); dept.setCreateTime(LocalDateTime.now()); dept.setUpdateTime(LocalDateTime.now()); deptService.insertDept(dept); } @Test public void updateDept(){ Dept dept = new Dept(); dept.setId(5); dept.setName("外交部"); deptService.updateDept(dept); } }
Spring注解
@Component @Controller @Service @Repository | 标注在自己开发的类上,用于将当前类对象放入Spring容器 |
---|---|
@Bean | 标注在配置类中的方法上,用于将方法的返回值对象放入Spring容器 |
@Autowired | 标注在类中的属性上,用于从Spring容器中获取属性的值 |
@Qualifier @Primary | 依赖注入时,根据一个接口查到了多个对象,使用这两个注解确定使用哪个对象 |
@Scope | 标注在类上,声明对象是单例还是多例 |
@Configuration | 标注在配置类上,Spring容器启动时会自动加载类中的配置 |
@ComponentScan | 标注在主配置类上,用于声明包扫描的范围 |
动态代理
JDK动态代理
被代理对象需要有接口
// jdk动态代理工厂 public class JDKProxyFactory { public static Object getProxy(Class clazz) { // 生成代理对象 Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Logger logger = new Logger(); Object obj = null; try { // 前置增强 logger.m1(); // method.invoke:被代理的对象执行方法 obj = method.invoke(clazz.newInstance(), args); // 后置增强 logger.m2(); } catch (Exception e) { e.printStackTrace(); logger.m3(); } return obj; } }); return proxy; } }
Cglib动态代理
被代理对象可以被继承
// Cglib动态代理 (基于继承) public class CglibProxyFactory { public static Object getProxy(Class clazz){ // 创建一个代理工具类 Enhancer enhancer = new Enhancer(); // 根据父类创建子类对象(认爹) enhancer.setSuperclass(clazz); // 调用代理对象的方法 (增强的代码写在这里) enhancer.setCallback(new InvocationHandler() { @Override // 参数一:代理对象本身(用不上) // 参数二:代理对象方法 // 参数三:方法的参数 public Object invoke(Object o, Method method, Object[] objects) throws Throwable { Logger logger = new Logger(); // 前置增强 logger.m1(); // 执行被代理对象的方法 Object invoke = method.invoke(clazz.newInstance(), objects); // 后置增强 logger.m2(); return invoke; // 返回方法执行的返回值 } }); // 生成代理对象并返回 Object proxy = enhancer.create(); return proxy; } }
Spring事务
事务配置
方法上加上@Transactional(rollbackFor = Exception.class)
配置类加上@EnableTransactionManagement 和 DataSourceTransactionManager bean
@Service public class AccountServiceImpl implements AccountService { @Autowired private AccountMapper accountMapper; @Override // @Transactional加在方法上,当前方法开启事务,默认只回滚运行时异常 // rollbackFor:指定回滚的异常 // @Transactional(rollbackFor = Exception.class) :标准写法 @Transactional(rollbackFor = Exception.class) public void transfer(String out, String in, Float money) throws Exception { // 1. 扣钱 accountMapper.subtract(out, money); // 模拟异常 // System.out.println(1/0); if (true){ throw new Exception(); } // 2. 加钱 accountMapper.add(in, money); } }
//配置类 @ComponentScan("com.itheima") @EnableTransactionManagement // 开启事务管理 public class SpringConfig { //配置mapper位置 @Bean public MapperScannerConfigurer scannerConfigurer(){ MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer(); scannerConfigurer.setBasePackage("com.itheima.mapper");//设置接口包位置 return scannerConfigurer; } //配置数据源 @Bean public DruidDataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/spring"); dataSource.setUsername("root"); dataSource.setPassword("1234"); return dataSource; } //配置SqlSessionFactory @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DruidDataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource);//设置数据源 return sqlSessionFactoryBean; } // 把数据库事务管理器加入IOC容器 @Bean public DataSourceTransactionManager transactionManager(DruidDataSource dataSource){ DataSourceTransactionManager manager = new DataSourceTransactionManager(); manager.setDataSource(dataSource); return manager; } }
SpringMVC
SpringMCV注解总结
分如下两档
永远的神
-
@RestController
写在类上,@RestController = @Controller + @ResponseBody
-
@GetMapping
Get请求URL
-
@PostMapping
Post请求URL
-
@RequestParam
1.参数映射
2.参数设置默认值
有时用到
-
@Controller
-
@RequestMapping
-
@PutMapping
-
@DeleteMapping
-
@PathVariable
-
@ResponseBody
-
@DateTimeFormat
-
@RestControllerAdvice
-
@ExceptionHandler
-
// web层IOC注入注解 @Controller
// 请求路径方式一: @RequestMapping
// 请求路径方式二: Restful 四兄弟 @PostMapping @PutMapping @GetMapping @DeleteMapping
// 返回json格式数据 @ResponseBody
// 接收json格式参数 @RequestBody
// 1.参数映射 2.参数设置默认值 @RequestParam
// 设置日期格式 @DateTimeFormat
// 写在类上,@RestController = @Controller + @ResponseBody @RestController
// 通过请求路径直接传递参数 @PathVariable
// 标注在类上,声明当前类是一个用于专门处理异常的类 @RestControllerAdvice
// 标注在方法上,声明当前方法可以处理哪些异常 @ExceptionHandler
//@Controller //将当前类的对象放入容器 //@ResponseBody //将方法的返回值(对象和集合)转json返回 @RestController //@RequestMapping("/user") 如果@RequestMapping标注在类上, 代表提取公共请求路径 public class UserController { @RequestMapping("/user/demo1") // 给当前方法绑定一个请求地址 //@ResponseBody //将方法的返回值(对象和集合)转json返回 public User demo1(String name, Integer age) { User user = new User(name, age); return user; } // value: 等同于path, 用于给当前方法绑定请求路径 // 支持数组的写法, 代表可以为一个方法绑定多个请求路径 @RequestMapping(value = {"/user/demo2", "/user/demo3"}) //@ResponseBody public String demo2() { return "ok"; } // method: 限制请求类型,支持数组的写法, 代表可以同时允许多种请求方式 // 如果method属性省略, 代表的是全部方式都可以 @RequestMapping(value = "/user/demo4", method = {RequestMethod.GET, RequestMethod.POST}) //@ResponseBody public String demo4() { return "ok"; } // 使用简单类型接收 需要保证前端传递的参数名称跟方法的形参名称一致 // 对于一些简单类型的数据, mvc底层是可以自动完成转换的 @RequestMapping("/user/demo5") public String demo5(String name, Integer age) { System.out.println("name:" + name + ",age:" + age); return "ok"; } // 使用对象类型接收 需要保证前端传递的参数名称跟对象属性名称一致 @RequestMapping("/user/demo6") public String demo6(User user) { System.out.println(user); return "ok"; } // 使用数组类型接收 需要保证前端传递的参数名称跟方法的形参名称一致 @RequestMapping("/user/demo7") public String demo7(String[] hobby) { System.out.println(Arrays.toString(hobby)); return "ok"; } // 使用集合类型接收 需要保证前端传递的参数名称跟方法的形参名称一致 // 如果直接使用List来接收请求参数, 需要在参数类型之前加入@RequestParam @RequestMapping("/user/demo8") public String demo8(@RequestParam("hobby") List<String> hobby) { System.out.println(Arrays.toString(hobby.toArray())); return "ok"; } // 使用日期类型接收 // 在参数之前,使用@DateTimeFormat(pattern = "自定义日期格式") @RequestMapping("/user/demo9") public String demo9(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date updateTime) { System.out.println(updateTime); return "ok"; } // 接收请求体中的json数据 // 在方法形参中去声明一个对象, 在对象之前使用@RequestBody注解 // 此注解就会从请求体中接收json字符串, 并且按照给的类型进行转换 // @RequestMapping("/user/demo10") // public String demo10(@RequestBody User user) { // System.out.println(user); // // return "ok"; // } @RequestMapping("/user/demo10") public String demo10(@RequestBody Map<String, Object> map) { System.out.println(map.get("name")); System.out.println(map.get("age")); return "ok"; } // 接收请求路径中的参数 //@PathVariable("name") 从请求路径上获取参数 注意: 路径上使用{}做占位符 @RequestMapping("/user/demo11/name/{name}/age/{age}") public String demo11( @PathVariable("name") String name, @PathVariable("age") Integer age ) { System.out.println("name:" + name + ",age:" + age); return "ok"; } //@RequestParam 有3个使用场景 // 1. 当请求中参数名称跟方法形参名称不对应的时候,可以使用此注解的name属性完成映射 // 2. 当请求中有可能不传递的参数时, 可以使用此注解的defaultValue属性设置默认值 // 3. 接收List集合 // 注意:@RequestParam标注的参数默认情况下不能为空, 如果想取消这个限制,使用required = false @RequestMapping("/user/demo12") public String demo12( @RequestParam(name = "name", required = false) String username, @RequestParam(defaultValue = "18") Integer age ) { System.out.println("username:" + username); System.out.println("age:" + age); return "ok"; } // 模拟异常 @RequestMapping("/user/demo21") public void demo21(Integer type) { if (type == 1) { // ArithmeticException int i = 1 / 0; } else if (type == 2) { // NullPointerException String s = null; System.out.println(s.length()); } else { // ArrayIndexOutOfBoundsException Integer[] arr = new Integer[2]; arr[5] = 1; } } // restful--添加 //@RequestMapping(value = "/users", method = RequestMethod.POST) @PostMapping("/users") // 仅仅接收post请求 public void save(@RequestBody User user) { System.out.println(user); } // restful--查询列表 //@RequestMapping(value = "/users", method = RequestMethod.GET) @GetMapping("/users") // 仅仅接收get请求 public List<User> findAll() { List<User> list = new ArrayList<>(); list.add(new User("张三", 18)); list.add(new User("李四", 19)); return list; } // 主键删除 @DeleteMapping("/users/{id}") public void deleteById(@PathVariable("id") Integer id) { System.out.println("删除id:" + id); } // 主键查询 @GetMapping("/users/{id}") public void findById(@PathVariable("id") Integer id) { System.out.println("查询id:" + id); } }
SpringBoot
启动类
// 启动类,位置必须高于其他类 @SpringBootApplication // 启动类注解 @MapperScan("com.itheima.mapper") // 指定mapper接口所在的包 @EnableAspectJAutoProxy // 开启aop @ServletComponentScan public class SpringBootDemoApplication { // springboot内置了tomcat,端口号默认8080 (约定大于配置) public static void main(String[] args) { // 固定写法 // 参数一:启动类.class // 参数二:args SpringApplication.run(SpringBootDemoApplication.class, args); } }
yml样例
# 大小写敏感 # 缩进表示层级关系 # 参数值和冒号一定要有空格 # #代表注释 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot username: root password: 1234 mybatis: configuration: # 开启驼峰映射 map-underscore-to-camel-case: true # 日志级别( debug info warn error ) logging: level: com.itheima: info file: name: D:\\spring.log # 只允许本地IP访问 server: address: 127.0.0.1
读取配置
# 大小写敏感 # 缩进表示层级关系 # 参数值和冒号一定要有空格 # #代表注释 server: port: 8081 servlet: context-path: /itheima # 对象:键值对的集合 user: username: '张三' password: '123456' addressList: # 数组:固定写法 - '杭州' - '北京' - '上海'
读取配置一
@RestController public class IndexController { // @Value("${配置路径}"):读取配置 @Value("${user.username}") private String username; @Value("${user.password}") private String password; @GetMapping("/index") public String index() { return "hello spring boot"; } }
读取配置二
@Data @Configuration // 配置类 @ConfigurationProperties(prefix = "user") // 读取配置文件中指定配置 public class UserConfig { private String username; private String password; private List<String> addressList; }
@RestController public class IndexController { @Autowired private UserConfig userConfig; @GetMapping("/index") public String index() { System.out.println(userConfig); return "hello spring boot"; } }
springboot增删改查
@Data @AllArgsConstructor @NoArgsConstructor public class Student { private Long id; private String number; private String userName; @JsonFormat(pattern = "yyyy-MM-dd",timezone = "Asia/Shanghai") private Date birthday; private String address; }
@Repository public interface StudentMapper { @Select("select id,number,user_name,birthday,address from tb_student") List<Student> findAll(); @Delete("delete from tb_student where id=#{id}") void deleteById(@Param("id") Long id); @Select("select id,number,user_name,birthday,address from tb_student where " + "id =#{id}") Student findById(@Param("id") Long id); @Update("update tb_student set number = #{student.number},user_name=#{student.userName},birthday=#{student.birthday},address=#{student.address} where id=#{student.id}") void updateById(@Param("student") Student student); @Insert("insert into tb_student values (#{student.id},#{student.number},#{student.userName},#{student.birthday},#{student.address})") void save(@Param("student") Student student); }
public interface StudentService { List<Student>findAll(); void deleteById(Long id); Student findById(Long id); void updateById(Student student); void save(Student student); }
@Service public class StudentServiceImpl implements StudentService { @Autowired private StudentMapper studentMapper; @Override public List<Student> findAll() { return studentMapper.findAll(); } @Override public void deleteById(Long id) { studentMapper.deleteById(id); } @Override public Student findById(Long id) { return studentMapper.findById(id); } @Override public void updateById(Student student) { studentMapper.updateById(student); } @Override public void save(Student student) { studentMapper.save(student); } }
@RestController public class StudnetContrroller { @Autowired private StudentService studentService; @GetMapping("/student") public Result findAll() { List<Student> students = studentService.findAll(); return Result.success(students); } @DeleteMapping("/student/{id}") public Result deleteById(@PathVariable("id") Long id){ studentService.deleteById(id); return Result.success(); } @GetMapping("/student/{id}") public Result findById(@PathVariable("id") Long id){ Student student = studentService.findById(id); return Result.success(student); } @PutMapping("/student") public Result updateById(@RequestBody Student student){ studentService.updateById(student); return Result.success(); } @PostMapping("/student") public Result add(@RequestBody Student student){ studentService.save(student); return Result.success(); } }
分页查询
<!--mybatis分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency>
@GetMapping("/emps") public Result findByPage(@RequestParam("page") Integer page, @RequestParam("pageSize") Integer pageSize, @RequestParam(value = "name", required = false) String name, @RequestParam(value = "gender", required = false) Integer gender, @RequestParam(value = "begin", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, @RequestParam(value = "end", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) { PageBean<Emp> pageBean = empService.findByPage(page, pageSize, name, gender, begin, end); return Result.success(pageBean); }
@Service public class EmpServiceImpl implements EmpService { @Autowired private EmpMapper empMapper; @Override public PageBean<Emp> findByPage(Integer page, Integer pageSize, String name, Integer gender, LocalDate begin, LocalDate end) { // 1 设置分页参数 PageHelper.startPage(page,pageSize); // 2.执行查询 List<Emp> empList = empMapper.findList3(name, gender, begin, end); // 3.查询结果强转Page对象 Page<Emp> p = (Page<Emp>) empList; // 4. 封装对象返回 return new PageBean<>(p.getTotal(),p.getResult()); } }
@Mapper public interface EmpMapper { List<Emp> findList3(@Param("name") String name, @Param("gender") Integer gender, @Param("begin") LocalDate begin, @Param("end") LocalDate end); }
登录校验
<!--Token生成与解析--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
过滤器
@Slf4j @WebFilter("/*") public class LoginCheckFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 1. 请求和响应对象的强转 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 2. 获取请求uri // uri:/login url: http://localhost:90/login String uri = request.getRequestURI(); log.info("uri的值是: {}", uri); // 3. 判断请求uri是否是登录,如果是就放行 if ("/login".equals(uri)) { filterChain.doFilter(request, response); return; } // 4. 走到这行一定不是登录,获取请求头的token (令牌) String token = request.getHeader("token"); // 5. token不存在,返回前端未登录 if (token == null || token.equals("")) { String json = new ObjectMapper().writeValueAsString(Result.error("NOT_LOGIN")); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json); return; } // 6. 解析token,如果解析失败,返回前端未登录 try { JwtUtils.parseJWT(token); } catch (Exception e) { // 出现异常,token校验不通过,返回错误提示 String json = new ObjectMapper().writeValueAsString(Result.error("NOT_LOGIN")); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json); return; } // 7. 放行 filterChain.doFilter(request, response); } }
// 启动类,位置必须高于其他类 @SpringBootApplication // 启动类注解 @MapperScan("com.itheima.mapper") // 指定mapper接口所在的包 @EnableAspectJAutoProxy // 开启aop @ServletComponentScan // 开启过滤器 public class SpringBootDemoApplication { // springboot内置了tomcat,端口号默认8080 (约定大于配置) public static void main(String[] args) { // 固定写法 // 参数一:启动类.class // 参数二:args SpringApplication.run(SpringBootDemoApplication.class, args); } }
拦截器
@Component public class LoginCheckInterceptor implements HandlerInterceptor { @Autowired private Gson gson; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 请求头中拿令牌 String token = request.getHeader("token"); // 如果字符串没有长度(null 或 空串),返回登录错误 // org.springframework.util.StringUtils if (!StringUtils.hasLength(token)){ String json = gson.toJson(Result.error("NOT_LOGIN")); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json); // 返回浏览器错误提示 return false; } try { JwtUtils.parseJWT(token); } catch (Exception e) { String json = gson.toJson(Result.error("NOT_LOGIN")); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json); // 返回浏览器错误提示 return false; // 禁止通行 } // 放行 return true; } }
@Configuration public class MvcConfig implements WebMvcConfigurer { @Autowired private LoginCheckInterceptor loginCheckInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginCheckInterceptor) .addPathPatterns("/**") .excludePathPatterns("/login"); } }
日志记录
<!--添加依赖--> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> <!--aop--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
@Retention(RetentionPolicy.RUNTIME) // 保留到运行阶段 @Target({ElementType.METHOD}) // 只能加方法上 public @interface LogAnno { String methodDesc() default ""; // 方法用途 }
// 查询员工 @GetMapping("/emps/{id}") @LogAnno(methodDesc = "查询员工") public Result findById(@PathVariable("id") Integer id) { Emp emp = empService.findById(id); return Result.success(emp); } // 修改员工 @PutMapping("/emps") @LogAnno(methodDesc = "修改员工") public Result update(@RequestBody Emp emp) { empService.update(emp); return Result.success(); } // 新增员工 @PostMapping("/emps") @LogAnno(methodDesc = "新增员工") public Result save(@RequestBody Emp emp) { empService.save(emp); return Result.success(); }
// 记录日志的切面 @Aspect @Component public class LogAspect { @Autowired private HttpServletRequest request; @Autowired private OperateLogMapper operateLogMapper; // 切点 (标注LogAnno注解的方法都被切中) @Pointcut("@annotation(com.itheima.anno.LogAnno)") public void pt() { } // 环绕通知 @Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable { OperateLog operateLog = new OperateLog(); // 获取类名 operateLog.setClassName(pjp.getTarget().getClass().getName()); // 获取方法参数 operateLog.setMethodParams(Arrays.toString(pjp.getArgs())); // org.aspectj.lang.reflect.MethodSignature MethodSignature ms = (MethodSignature) pjp.getSignature(); // 获取方法名 operateLog.setMethodName(ms.getMethod().getName()); LogAnno logAnno = ms.getMethod().getAnnotation(LogAnno.class); // 获取方法描述 operateLog.setMethodDesc(logAnno.methodDesc()); String token = request.getHeader("token"); Claims claims = JwtUtils.parseJWT(token); // 拿到用户id Integer id = claims.get("id", Integer.class); operateLog.setOperateUser(id); // 操作时间 operateLog.setOperateTime(LocalDateTime.now()); long begin = System.currentTimeMillis(); Object object = pjp.proceed(); long end = System.currentTimeMillis(); // 方法执行时间 operateLog.setCostTime(end - begin); // 方法返回信息 operateLog.setReturnValue(object.toString()); // 落库 operateLogMapper.insert(operateLog); return object; } }
图片上传
<!--阿里云--> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.10.2</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.0.6</version> </dependency>
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/tlias username: root password: 1234 servlet: multipart: max-file-size: 10MB # 单个文件默认最大1MB max-request-size: 100MB # 一次多个文件默认最大10MB mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true aliyun: oss: endpoint: oss-cn-beijing.aliyuncs.com accessKeyId: LTAI5tQ823aWxiqK2BmKxyeN accessKeySecret: 0CYrcWVlpnNJdm7Zn1R53iyEcXbGbM bucketName: tlias-web-management-yn url: https://tlias-web-management-yn.oss-cn-beijing.aliyuncs.com
@Data @Component @ConfigurationProperties(prefix = "aliyun.oss") // 读取配置信息 public class OssProperties { // 服务器区域 private String endpoint; // 秘钥key private String accessKeyId; // 秘钥 private String accessKeySecret; // 存储空间 private String bucketName; // 访问域名 private String url; }
//阿里存储工具类 @Component public class OssTemplate { //注入配置参数实体类对象 @Autowired private OssProperties ossProperties; //文件上传 public String upload(String fileName, InputStream inputStream) { //创建客户端 OSS ossClient = new OSSClientBuilder().build(ossProperties.getEndpoint(), ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret()); //设置文件最终的路径和名称 String objectName = "images/" + new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/" + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf(".")); //meta设置请求头,解决访问图片地址直接下载 ObjectMetadata meta = new ObjectMetadata(); meta.setContentType(getContentType(fileName.substring(fileName.lastIndexOf(".")))); //上传 ossClient.putObject(ossProperties.getBucketName(), objectName, inputStream, meta); //关闭客户端 ossClient.shutdown(); return ossProperties.getUrl() + "/" + objectName; } //文件后缀处理 private String getContentType(String FilenameExtension) { if (FilenameExtension.equalsIgnoreCase(".bmp")) { return "image/bmp"; } if (FilenameExtension.equalsIgnoreCase(".gif")) { return "image/gif"; } if (FilenameExtension.equalsIgnoreCase(".jpeg") || FilenameExtension.equalsIgnoreCase(".jpg") || FilenameExtension.equalsIgnoreCase(".png")) { return "image/jpg"; } return "image/jpg"; } }
@RestController public class FileController { @Autowired private OssTemplate ossTemplate; // 文件上传 @PostMapping("/upload") public Result uploadFile(MultipartFile image) throws IOException { // 调用阿里云工具类上传文件 String filePath = ossTemplate.upload(image.getOriginalFilename(), image.getInputStream()); // 返回文件路径 return Result.success(filePath); } }