Day47(17)-F:\硕士阶段\Java\课程代码\后端\web-ai-code\web-ai-project02

文件上传

服务器搭建

image-20251124131949006

云服务

image-20251124132052428

image-20251124132218894

image-20251124132551370

LTAI5t5hEbLq6RPAc5ihjWEH
vY9VD0hh9q7TcFU2H9AP9x8FPEA8hr
配置环境变量
set OSS_ACCESS_KEY_ID=LTAI5t5hEbLq6RPAc5ihjWEH
set OSS_ACCESS_KEY_SECRET=vY9VD0hh9q7TcFU2H9AP9x8FPEA8hr
设置使其生效
setx OSS_ACCESS_KEY_ID "%OSS_ACCESS_KEY_ID%"
setx OSS_ACCESS_KEY_SECRET "%OSS_ACCESS_KEY_SECRET%"
验证是否生效
echo %OSS_ACCESS_KEY_ID%
echo %OSS_ACCESS_KEY_SECRET%

阿里云搭建相关视频在107集

image-20251124142322620

image-20251124142401623

package com.itheima.utils;

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@Component
public class AliyunOSSOperator {

    private String endpoint = "https://oss-cn-beijing.aliyuncs.com";
    private String bucketName = "java-ai-01-david";
    private String region = "cn-beijing";

    public String upload(byte[] content, String originalFilename) throws Exception {
        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

        // 填写Object完整路径,例如202406/1.png。Object完整路径中不能包含Bucket名称。
        //获取当前系统日期的字符串,格式为 yyyy/MM
        String dir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
        //生成一个新的不重复的文件名
        String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String objectName = dir + "/" + newFileName;

        // 创建OSSClient实例。官方代码
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region(region)
                .build();

        try {
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content));
        } finally {
            ossClient.shutdown();
        }

        return endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + objectName;
    }

}
package com.itheima.controller;

import com.itheima.pojo.Result;
import com.itheima.utils.AliyunOSSOperator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

@Slf4j
@RestController
public class UploadController {
//    /**
//     * 本地磁盘存储,不推荐
//     * @param name
//     * @param age
//     * @param file
//     * @return
//     * @throws IOException
//     */
//    @PostMapping("/upload")
//    public Result upload(String name, Integer age, MultipartFile file) throws IOException {
//        log.info("接收的参数:{},{},{}",name,age,file);
//        //获取原始文件名
//        String originalFilename = file.getOriginalFilename();
//        //新的文件名
//        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
//        String newFileName = UUID.randomUUID().toString() + extension;
//        //保存文件
//        //file.transferTo(new File("F:/images/"+originalFilename));
//        file.transferTo(new File("F:/images/"+newFileName));
//        return Result.success();
//    }
    @Autowired
    private AliyunOSSOperator aliyunOSSOperator;
    @PostMapping("/upload")
    public Result upload(MultipartFile file) throws Exception {
        log.info("文件上传:{}",file.getOriginalFilename());

        //将文件交给OSS存储管理
        String url = aliyunOSSOperator.upload(file.getBytes(), file.getOriginalFilename());
        log.info("文件上传到OSS,url:"+url);

        return Result.success(url);
    }
}

image-20251124151437001

image-20251124151642118

#阿里云OSS
aliyun:
  oss:
    endpoint: https://oss-cn-beijing.aliyuncs.com
    bucketName: java-ai-01-david
    region: cn-beijing
package com.itheima.utils;

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@Component
public class AliyunOSSOperator {

//    private String endpoint = "https://oss-cn-beijing.aliyuncs.com";
//    private String bucketName = "java-ai-01-david";
//    private String region = "cn-beijing";
    @Value("${aliyun.oss.endpoint}")
    private String endpoint ;
    @Value("${aliyun.oss.bucketName}")
    private String bucketName ;
    @Value("${aliyun.oss.region}")
    private String region ;

    public String upload(byte[] content, String originalFilename) throws Exception {
        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

        // 填写Object完整路径,例如202406/1.png。Object完整路径中不能包含Bucket名称。
        //获取当前系统日期的字符串,格式为 yyyy/MM
        String dir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
        //生成一个新的不重复的文件名
        String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String objectName = dir + "/" + newFileName;

        // 创建OSSClient实例。官方代码
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region(region)
                .build();

        try {
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content));
        } finally {
            ossClient.shutdown();
        }

        return endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + objectName;
    }

}

image-20251124152520710

image-20251124152632534

image-20251124153020232

package com.itheima.utils;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSProperties {
    private String endpoint;
    private String bucketName;
    private String region;
}
package com.itheima.utils;

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@Component
public class AliyunOSSOperator {

//    private String endpoint = "https://oss-cn-beijing.aliyuncs.com";
//    private String bucketName = "java-ai-01-david";
//    private String region = "cn-beijing";

    //方式一:提供@Value注解将属性逐一注入
//    @Value("${aliyun.oss.endpoint}")
//    private String endpoint ;
//    @Value("${aliyun.oss.bucketName}")
//    private String bucketName ;
//    @Value("${aliyun.oss.region}")
//    private String region ;
    //方式二:
    @Autowired
    private AliyunOSSProperties aliyunOSSProperties;

    public String upload(byte[] content, String originalFilename) throws Exception {
        String endpoint = aliyunOSSProperties.getEndpoint();
        String bucketName = aliyunOSSProperties.getBucketName();
        String region = aliyunOSSProperties.getRegion();

        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

        // 填写Object完整路径,例如202406/1.png。Object完整路径中不能包含Bucket名称。
        //获取当前系统日期的字符串,格式为 yyyy/MM
        String dir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
        //生成一个新的不重复的文件名
        String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String objectName = dir + "/" + newFileName;

        // 创建OSSClient实例。官方代码
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region(region)
                .build();

        try {
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content));
        } finally {
            ossClient.shutdown();
        }

        return endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + objectName;
    }

}

image-20251124153707914

删除员工

image-20251124154052815

@DeleteMapping
public  Result delete(@RequestParam List<Integer> ids){
    log.info("批量删除员工:{}", ids);
    empService.delete(ids);
    return Result.success();
}
@Transactional(rollbackFor = {Exception.class})
@Override
public void delete(List<Integer> ids) {
    //删除员工的基本信息
    empMapper.deleteByIds(ids);

    //删除员工的工作经历信息
    empExprMapper.deleteByEmpIds(ids);

}
<?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.itheima.mapper.EmpExprMapper">
<!--    批量保存员工工作经历
        foreach:
                    collection:遍历的集合
                    item:遍历出来的元素的名字
                    separator:每次循环之间的分隔符
-->
    <insert id="insertBatch">
        insert into emp_expr(emp_id, begin, end, company, job) VALUES
    <foreach collection="exprList" item="expr" separator=",">
        (#{expr.empId},#{expr.begin},#{expr.end},#{expr.company},#{expr.job})
    </foreach>
    </insert>
    <delete id="deleteByEmpIds">
        delete from emp_expr where emp_id in
        <foreach collection="empIds" item="Id" separator="," open="(" close=")">
            #{Id}
        </foreach>
    </delete>
</mapper>
<?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.itheima.mapper.EmpMapper">
<!--    批量删除员工的基本信息-->
    <delete id="deleteByIds">
        delete from emp where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>
    <select id="list" resultType="com.itheima.pojo.Emp">
        select e.*,d.name deptName from emp e left join dept d on e.dept_id = d.id
        <where>
            <if test="name != null and name != ''">
                e.name like concat('%',#{name},'%')
            </if>
            <if test="gender != null">
                and e.gender = #{gender}
            </if>
            <if test="begin != null and end != null">
                and entry_date between #{begin} and #{end}
            </if>
        </where>
            order by e.update_time desc
    </select>
</mapper>

Spring 对List类型的参数,无论参数名是否一致,都必须显式添加@RequestParam注解才能完成绑定,不存在 “省略注解也能生效” 的情况。下面详细拆解原因和修正方案。

一、为什么这段代码不加注解会失效?

  1. Spring 的参数绑定规则限制

    Spring 的无注解自动绑定仅支持基本类型、包装类、String、数组等「简单类型」,而List集合容器类型,不在自动绑定的范围内。即使参数名names与前端传入的参数名完全一致,Spring 也无法识别 “将请求中的names参数封装为List<String>”,最终入参names会是null

  2. 实际运行的表现

    若执行这段代码,前端请求/list?names=张三&names=李四,后端接收到的names值为null,返回结果会是List参数:null,而非预期的List参数:[张三, 李四]

二、正确的写法(必须加@RequestParam

想要让List<String> names正确绑定前端参数,必须显式添加@RequestParam注解,这是唯一的正确方式:java运行

@GetMapping("/list")
public String getList(@RequestParam List<String> names) {
    return "List参数:" + names;
}

此时前端请求/list?names=张三&names=李四,后端才能正确接收并封装为List<String>,返回List参数:[张三, 李四]

三、容易混淆的 “例外情况”:数组类型可省略注解

如果将入参改为数组类型String[] names,则可以省略@RequestParam,因为数组属于 Spring 的「简单类型」,支持无注解自动绑定:java运行

// ✅ 数组类型:参数名一致,可省略@RequestParam,绑定正常
@GetMapping("/list")
public String getList(String[] names) {
    return "数组参数:" + Arrays.toString(names);
}

前端请求/list?names=张三&names=李四,会返回数组参数:[张三, 李四],这是数组与 List 的核心区别,也是最容易产生误解的点。

image-20251124163513366

修改员工

  1. 查询回显
  2. 修改数据

image-20251124165159019

image-20251124170829269

image-20251124171233043

image-20251124171613175

<?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.itheima.mapper.EmpMapper">
<!--    批量删除员工的基本信息-->
    <delete id="deleteByIds">
        delete from emp where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>
    <select id="list" resultType="com.itheima.pojo.Emp">
        select e.*,d.name deptName from emp e left join dept d on e.dept_id = d.id
        <where>
            <if test="name != null and name != ''">
                e.name like concat('%',#{name},'%')
            </if>
            <if test="gender != null">
                and e.gender = #{gender}
            </if>
            <if test="begin != null and end != null">
                and entry_date between #{begin} and #{end}
            </if>
        </where>
            order by e.update_time desc
    </select>

<!--    定义resultMap-->
    <resultMap id="empResultMap" type="com.itheima.pojo.Emp">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="name" property="name"/>
        <result column="gender" property="gender"/>
        <result column="phone" property="phone"/>
        <result column="job" property="job"/>
        <result column="salary" property="salary"/>
        <result column="image" property="image"/>
        <result column="entry_date" property="entryDate"/>
        <result column="dept_id" property="deptId"/>
        <result column="create_time" property="createTime"/>

<!--        封装工作经历-->
        <collection property="exprList" ofType="com.itheima.pojo.EmpExpr">
            <id column="ee_id"  property="id"/>
            <result column="ee_empid"  property="empId"/>
            <result column="ee_begin"  property="begin"/>
            <result column="ee_end"  property="end"/>
            <result column="ee_company"  property="company"/>
            <result column="ee_job"  property="job"/>
        </collection>
    </resultMap>
<!--    根据ID查询员工的基本信息和员工基本工作经历信息-->
    <select id="getById" resultMap="empResultMap">
        select
            e.*,
            ee.id ee_id,
            ee.emp_id ee_empid,
            ee.begin ee_begin,
            ee.end ee_end,
            ee.company ee_company,
            ee.job ee_job
        from emp e left join emp_expr ee on e.id = ee.emp_id
        where e.id =#{id};
    </select>
</mapper>

image-20251124173549125

image-20251124174309563

@PutMapping
public Result update(@RequestBody Emp emp){
    log.info("修改员工:{}",emp);
    empService.update(emp);
    return Result.success(emp);
}
/**
 * 修改员工
 * @param emp
 */
@Transactional(rollbackFor = {Exception.class})
@Override
public void update(Emp emp) {
    //1.根据ID修改员工的基本信息
    emp.setUpdateTime(LocalDateTime.now());
    empMapper.updateById(emp);
    //2.根据ID修改员工的工作经历信息
    //2.1先根据员工ID删除原有的工作经历
    empExprMapper.deleteByEmpIds(Arrays.asList(emp.getId()));
    //2.2再添加
    List<EmpExpr> exprList = emp.getExprList();
    if (!CollectionUtils.isEmpty(exprList)){
        exprList.forEach(empExpr -> {
            empExpr.setEmpId(emp.getId());
        });
        empExprMapper.insertBatch(exprList);
    }
}

优化

动态SQL

image-20251124181325226

<!--    根据ID更新员工基本工作经历信息-->
<!--    set标签:mybatis中动态标签:自动生成set关键字;会自动删除掉更新字段后多余的逗号-->
    <update id="updateById">
        UPDATE emp
        <set>
            <if test="username != null and username != ''">username = #{username},</if>
            <if test="password != null and password != ''">password = #{password},</if>
            <if test="name != null and name != ''">name = #{name},</if>
            <if test="gender != null">gender = #{gender},</if>
            <if test="phone != null and phone != ''">phone = #{phone},</if>
            <if test="job != null">job = #{job},</if>
            <if test="salary != null">salary = #{salary},</if>
            <if test="image != null and image != ''">image = #{image},</if>
            <if test="entryDate != null">entry_date = #{entryDate},</if>
            <if test="deptId != null">dept_id = #{deptId},</if>
            <if test="updateTime != null">update_time = #{updateTime}</if>
        </set>
        WHERE id = #{id}
    </update>

动态SQL标签

  1. :自动删掉多余逗号;自动生成set关键字
  2. :自动删掉and和or

image-20251124182757416

异常管理

信息统计

posted @ 2025-11-24 20:26  David大胃  阅读(7)  评论(0)    收藏  举报