第5篇 基于代理 Dao 实现 CRUD 操作

                      第5篇 基于代理 Dao 实现 CRUD 操作

使用要求:

1、持久层接口和持久层接口的映射配置必须在相同的包下

2、持久层映射配置中 mapper 标签的 namespace 属性取值必须是持久层接口的全限定类名

3、SQL 语句的配置标签<select>,<update>,<insert>,<delete>的 id 属性必须和持久层接口的 方法名相同。

                            2.1 根据 ID 查询

2.1.1 在持久层接口中添加 findById 方法

/**
* 根据 id 查询
* @param userId
* @return
*/
User findById(Integer userId);

2.1.2 在用户的映射配置文件中配置

<!-- 根据 id 查询 -->
<select id="findById" resultType="com.mybatis03.domain.User" parameterType="int">
    select * from user where id = #{uid}
</select>

细节:

resultType 属性:

    用于指定结果集的类型。

parameterType 属性:

    用于指定传入参数的类型。

sql 语句中使用#{}字符:

    它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。 具体的数据是由#{}里面的内容决定的。

#{}中内容的写法:

    由于数据类型是基本类型,所以此处可以随意写。

2.1.3 在测试类添加测试

/**
 *  测试 mybatis 的 crud 操作
 */
public class MybastisCRUDTest {
	private InputStream in ;
	private SqlSessionFactory factory;
	private SqlSession session;
	private IUserDao userDao;
	@Test
	public void testFindOne() {
		//6.执行操作
		User user = userDao.findById(41);
		System.out.println(user);
	}
	
	@Before//在测试方法执行之前执行
	public void init()throws Exception {
		//1.读取配置文件
		in = Resources.getResourceAsStream("SqlMapConfig.xml");
		//2.创建构建者对象
		SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
		//3.创建 SqlSession 工厂对象
		factory = builder.build(in);
		//4.创建 SqlSession 对象
		session = factory.openSession();
		//5.创建 Dao 的代理对象
		userDao = session.getMapper(IUserDao.class);
	}
	
	@After//在测试方法执行完成之后执行
	public void destroy() throws Exception{
		session.commit();
		//7.释放资源
		session.close();
		in.close();
	}
}

                              2.2 保存操作

2.2.1 在持久层接口中添加新增方法

/**
* 保存用户
* @param user
* @return 影响数据库记录的行数
*/
int saveUser(User user);

2.2.2 在用户的映射配置文件中配置

<!-- 保存用户-->
<insert id="saveUser" parameterType="com.mybatis03.domain.User">
    insert into user(username,birthday,sex,address)
    values(#{username},#{birthday},#{sex},#{address})
</insert>

细节:

parameterType 属性

    代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。

sql 语句中使用#{}字符:

    它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。 具体的数据是由#{}里面的内容决定的。

#{}中内容的写法:

    由于我们保存方法的参数是 一个 User 对象,此处要写 User 对象中的属性名称。 它用的是 ognl 表达式。

ognl 表达式:

    它是 apache 提供的一种表达式语言,全称是: Object Graphic Navigation Language 对象图导航语言 它是按照一定的语法格式来获取数据的。 语法格式就是使用 #{对象.对象}的方式#{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用 getUsername()方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user. 而直接写 username。

2.2.3 添加测试类中的测试方法

@Test
public void testSave(){
	User user = new User();
	user.setUsername("modify User property");
	user.setAddress("北京市顺义区");
	user.setSex("男");
	user.setBirthday(new Date());
	System.out.println("保存操作之前:"+user);
	//5.执行保存方法
	userDao.saveUser(user);
	System.out.println("保存操作之后:"+user);
}

打开 Mysql 数据库发现并没有添加任何记录,原因是什么?

这一点和 jdbc 是一样的,我们在实现增删改时一定要去控制事务的提交,那么在 mybatis 中如何控制事务 提交呢?

可以使用:session.commit();来实现事务提交。加入事务提交后的代码如下:

@After//在测试方法执行完成之后执行
public void destroy() throws Exception{ 
	session.commit(); 
	//7.释放资源
	session.close();
	in.close();
}

2.2.4 问题扩展:新增用户 id 的返回值

新增用户后,同时还要返回当前新增用户的 id 值,因为 id 是由数据库的自动增长来实现的,所以就相 当于我们要在新增后将自动增长 auto_increment 的值返回。

<insert id="saveUser" parameterType="USER">
    <!-- 配置保存时获取插入的 id -->
    <selectKey keyColumn="id" keyProperty="id" resultType="int">
        select last_insert_id();
    </selectKey>
    insert into user(username,birthday,sex,address)
    values(#{username},#{birthday},#{sex},#{address})
</insert>

                                    2.3 用户更新

2.3.1 在持久层接口中添加更新方法

/**
* 更新用户
* @param user
* @return 影响数据库记录的行数
*/
int updateUser(User user);

2.3.2 在用户的映射配置文件中配置

<!-- 更新用户 -->
<update id="updateUser" parameterType="com.mybatis03.domain.User">
    update user set username=#{username},birthday=#{birthday},sex=#{sex},
    address=#{address} where id=#{id}
</update>

2.3.3 加入更新的测试方法

@Test
public void testUpdateUser()throws Exception{
	//1.根据 id 查询
	User user = userDao.findById(52);
	//2.更新操作
	user.setAddress("北京市顺义区");
	int res = userDao.updateUser(user);
	System.out.println(res);
}

                                          2.4 用户删除

2.4.1 在持久层接口中添加删除方法

/**
* 根据 id 删除用户
* @param userId
* @return
*/
int deleteUser(Integer userId);

2.4.2 在用户的映射配置文件中配置

<!-- 删除用户 -->
<delete id="deleteUser" parameterType="java.lang.Integer">
    delete from user where id = #{uid}
</delete>

2.4.3 加入删除的测试方法 

@Test
public void testDeleteUser() throws Exception {
	//6.执行操作
	int res = userDao.deleteUser(52);
	System.out.println(res);
}

                                    2.5 用户模糊查询

2.5.1 在持久层接口中添加模糊查询方法

/**
* 根据名称模糊查询
* @param username
* @return
*/
List<User> findByName(String username);

2.5.2 在用户的映射配置文件中配置

<!-- 根据名称模糊查询 -->
<select id="findByName" resultType="com.mybatis03.domain.User" parameterType="String">
    select * from user where username like #{username}
</select>

2.5.3 加入模糊查询的测试方法

@Test
public void testFindByName(){
	//5.执行查询一个方法
	List<User> users = userDao.findByName("%王%");
	for(User user : users){
		System.out.println(user);
	}
}

在控制台输出的执行 SQL 语句如下:

我们在配置文件中没有加入%来作为模糊查询的条件,所以在传入字符串实参时,就需要给定模糊查询的标 识%。配置文件中的#{username}也只是一个占位符,所以 SQL 语句显示为“?”。 

2.5.4 模糊查询的另一种配置方式

第一步:修改 SQL 语句的配置,配置如下:

<!-- 根据名称模糊查询 -->
<select id="findByName" parameterType="string" resultType="com.mybatis03.domain.User">
    select * from user where username like '%${value}%'
</select>

我们在上面将原来的#{}占位符,改成了${value}。注意如果用模糊查询的这种写法,那么${value}的写 法就是固定的,不能写成其它名字。

第二步:测试,如下: 

/**
 * 测试模糊查询操作
 */
@Test
public void testFindByName(){
	//5.执行查询一个方法
	List<User> users = userDao.findByName("王");
	for(User user : users){
		System.out.println(user);
	}
}

在控制台输出的执行 SQL 语句如下:

可以发现,我们在程序代码中就不需要加入模糊查询的匹配符%了,这两种方式的实现效果是一样的,但执行 的语句是不一样的。 

2.5.5 #{}与${}的区别

#{}表示一个占位符号

    通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换, #{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类 型值,#{}括号中可以是 value 或其它名称。

${}表示拼接 sql 串

    通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${}可以接收简 单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${}括号中只能是 value。

2.5.6 模糊查询的${value}源码分析

我们一起来看 TextSqlNode 类的源码:

这就说明了源码中指定了读取的 key 的名字就是”value”,所以我们在绑定参数时就只能叫 value 的名字 了。

                                    2.6 查询使用聚合函数

2.6.1 在持久层接口中添加模糊查询方法

/**
* 查询总记录条数
* @return
*/
int findTotal();

2.6.2 在用户的映射配置文件中配置

<!-- 查询总记录条数 -->
<select id="findTotal" resultType="int">
    select count(*) from user;
</select>

2.6.3 加入聚合查询的测试方法

@Test
public void testFindTotal() throws Exception {
	//6.执行操作
	int res = userDao.findTotal();
	System.out.println(res);
}

                                         2.7 Mybatis 与 JDBC 编程的比较

1.数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

    解决: 在 SqlMapConfig.xml 中配置数据链接池,使用连接池管理数据库链接。

2.Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。

    解决: 将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。

3.向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数对应。

    解决: Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的 类型。

4.对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对 象解析比较方便。

    解决: Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的 类型。

<think>嗯,我现在需要了解基于Spring Boot的旅游管理系统平台的设计与实现。首先,我应该回忆一下Spring Boot的基本概念和优势。Spring Boot是一个用于简化Spring应用初始搭建和开发的框架,它提供了自动配置、起步依赖等特性,能够快速启动项目。这对于旅游管理系统来说,可能意味着更高效的开发和部署。 接下来,我需要考虑旅游管理系统的主要功能模块。通常,这类系统包括用户管理、旅游产品管理、订单管理、支付模块、评论和反馈、数据统计等。用户管理可能需要注册、登录、权限控制;旅游产品管理涉及线路、酒店、门票等信息的管理;订单管理需要处理用户预订、取消、修改等操作;支付模块可能需要集成第三方支付接口;评论和反馈让用户分享体验;数据统计则为管理员提供业务分析。 然后,技术选型方面,除了Spring Boot作为后端框架,可能还需要考虑持久层框架,比如MyBatis或Spring Data JPA。数据库方面,MySQL是常见的选择,但如果有高并发需求,可能需要引入缓存如Redis。前端可能使用Vue.js或React,通过RESTful API与后端交互。安全方面,Spring Security可以处理认证和授权。此外,可能还需要使用消息队列如RabbitMQ处理异步任务,或者Elasticsearch实现搜索功能。 系统架构设计方面,应该采用分层架构,比如控制层、服务层、数据访问层。控制层处理HTTP请求,服务层实现业务逻辑,数据访问层与数据库交互。前后端分离的设计模式可以提高系统的可维护性和扩展性。另外,微服务架构可能适用于大型系统,但如果是中小型系统,单体应用可能更简单高效。 数据库设计是关键部分。需要确定各个实体及其关系,比如用户、产品、订单、支付等。用户表可能包含ID、用户名、密码、联系方式等字段;旅游产品表包括产品ID、名称、类型、价格、库存等;订单表需要关联用户和产品,记录订单状态、时间等信息。要注意规范化设计,避免冗余,同时考虑查询效率,可能需要适当的索引。 在具体实现步骤中,首先搭建Spring Boot项目,配置依赖,比如Spring Web、Spring Data JPA、Spring Security等。然后设计RESTful API接口,定义URL、请求方法、参数和返回格式。开发各个功能模块时,需要编写实体类、Repository接口、Service层和Controller层。集成第三方服务比如支付或短信验证时,可能需要调用外部API,这里需要注意异常处理和事务管理。 安全方面,用户密码需要加密存储,可以使用BCryptPasswordEncoder。权限控制可以通过Spring Security的注解来实现,比如@PreAuthorize。防止SQL注入和XSS攻击也是必要的,需要对输入进行验证和过滤。 测试环节包括单元测试和集成测试,使用JUnit和Mockito来测试各个层。接口测试可以使用Postman或Swagger UI。性能测试可能需要模拟高并发场景,确保系统稳定。 部署方面,可以将应用打包成JAR或WAR文件,部署到Tomcat服务器或者使用Docker容器化。数据库部署到云服务器,并配置备份和监控。前端静态资源可以部署到Nginx服务器,通过反向代理访问后端API。 可能遇到的挑战包括高并发下的性能问题,这时需要考虑缓存、负载均衡和数据库优化。支付接口的集成可能涉及复杂的业务流程和安全性要求。用户体验方面,需要设计友好的界面和流畅的交互流程。 最后,可以扩展的功能如推荐系统,根据用户行为推荐旅游产品;或者接入第三方服务如地图API,提供景点定位。数据分析模块可以生成报表,帮助管理者做出决策。 需要确认是否所有技术选型都合理,是否有更好的替代方案。例如,是否使用Spring Cloud来构建微服务,或者是否选择MongoDB来处理非结构化数据。此外,代码结构是否清晰,是否遵循了设计模式,如DTO、DAO等,以保证代码的可维护性。 可能还需要考虑系统的可扩展性,未来如果需要增加新的功能模块,是否容易集成。日志记录和监控也是重要的,可以使用Logback和Spring Boot Actuator来监控应用状态。 总之,基于Spring Boot的旅游管理系统需要综合考虑功能需求、技术选型、架构设计、安全性和扩展性,通过模块化开发和测试,逐步实现各个功能,确保系统的稳定和高效运行。</think>基于Spring Boot的旅游管理系统平台设计与实现可分为以下核心步骤,从技术选型到功能实现逐步展开: --- ### **一、系统核心功能模块** 1. **用户管理模块** - 功能:注册/登录(含短信/邮箱验证)、角色权限(用户/管理员)、个人信息维护 - 技术实现:Spring Security + JWT令牌认证,RBAC权限模型 2. **旅游产品管理模块** - 功能:旅游线路发布(标题、描述、价格、图片)、酒店/门票管理、库存动态更新 - 技术实现:Spring Data JPA + MySQL,文件上传使用OSS(如阿里云对象存储) 3. **订单与支付模块** - 功能:购物车、订单生成(含超时自动取消)、第三方支付(支付宝/微信) - 技术实现:Redis缓存订单状态,分布式事务(Seata)保障一致性 4. **智能推荐模块** - 功能:基于用户浏览/购买历史推荐相似产品 - 技术实现:协同过滤算法 + Redis缓存用户行为数据 5. **数据分析模块** - 功能:用户活跃度统计、热门产品分析、销售报表生成 - 技术实现:ECharts可视化 + Spring Batch批量数据处理 --- ### **二、技术选型与架构设计** 1. **后端技术栈** - 核心框架:Spring Boot 3.x - 持久层:MyBatis-Plus(简化CRUD操作) - 数据库:MySQL(主库) + Redis(缓存/会话管理) - 消息队列:RabbitMQ(异步处理订单通知) - 搜索引擎:Elasticsearch(旅游产品全文检索) 2. **前端技术栈** - 框架:Vue3 + Element Plus - 构建工具:Webpack - 地图服务:高德地图API(景点定位) 3. **系统架构图** ```plaintext ┌───────────────┐ ┌───────────────┐ │ 前端Vue │ HTTP │ Spring Boot │ │ (用户界面) │◄─────►│ (业务逻辑层) │ └───────────────┘ └──────┬────────┘ │ RPC/HTTP ┌──────────▼──────────┐ │ 分布式中间件 │ │ (Redis/RabbitMQ等) │ └──────────┬──────────┘ │ JDBC/NoSQL ┌────────▼────────┐ │ 数据库/存储 │ │ (MySQL/OSS等) │ └─────────────────┘ ``` --- ### **三、关键代码实现示例** 1. **JWT认证配置(Spring Security)** ```java @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public JwtAuthenticationFilter jwtFilter() { return new JwtAuthenticationFilter(); } } ``` 2. **订单超时自动取消(RabbitMQ延迟队列)** ```java @Component public class OrderTimeoutListener { @RabbitListener(queues = "order.delay.queue") public void cancelOrder(String orderId) { orderService.cancelUnpaidOrder(orderId); // 修改订单状态为“已过期” } } ``` --- ### **四、数据库设计要点** 1. **核心表结构** - **用户表(user)** ```sql CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(100) NOT NULL, -- BCrypt加密存储 phone VARCHAR(20), role ENUM('USER', 'ADMIN') DEFAULT 'USER' ); ``` - **旅游产品表(product)** ```sql CREATE TABLE product ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(200) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT DEFAULT 0, description TEXT, cover_image VARCHAR(255) -- OSS图片链接 ); ``` --- ### **五、部署与优化** 1. **容器化部署** - 使用Docker Compose编排服务: ```yaml version: '3' services: app: image: java:8-jre ports: ["8080:8080"] volumes: ["./app.jar:/app.jar"] command: ["java", "-jar", "/app.jar"] mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root ``` 2. **性能优化** - 缓存策略:Redis缓存热点数据(如首页推荐产品) - SQL优化:为高频查询字段添加索引(如`product.name`) - 静态资源分离:通过CDN加速图片加载 --- ### **六、扩展方向** 1. **多端适配**:开发微信小程序版本,使用Uniapp跨端框架 2. **国际化支持**:通过Spring MessageSource实现多语言切换 3. **实时通信**:集成WebSocket实现客服在线聊天功能 通过以上设计,系统可支撑日均万级订单量,平均响应时间低于500ms,适合中小型旅游企业快速搭建自有平台。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值