0%

说明

  • 本篇开始学习springmvc的知识
  • win10,idea社区版本,java1.8

SpringMVC

  • Spring MVC 本身就是 Spring 框架的一部分,可以说和 Spring 框架是无缝集成。性能方面具有先天的优越性,是当今业界最主流的 Web 开发框架

MVC

  • 控制层(Controller):负责接收并转发请求,对请求进行处理后,指定视图并将响应结果发送给客户端。
  • 视图层(View):负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、界面设计等功能。
  • 数据模型层(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务规则)的处理和实现数据操作(即在数据库中存取数据)

执行流程

image-20240218091941742

实例

  • 依赖文件如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

<packaging>war</packaging>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.2.16.RELEASE</spring.version>

</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- Spring Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- servlet api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>

</project>

将pom.xml中的package改为war,加入springMVC,就是web工程

war 这里默认是pom,我改成了war,经过测试没有区别

  • 在src->main下新建webapp目录

  • 在webapp下新建WEB-INF目录,WEB-INF下新建web.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--编码过滤器,解决post乱码问题-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
<!--中央控制器,用于处理请求-->
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-mvc.xml</param-value>
</init-param>
<!--启动级别 1 表示服务器启动,这个项目也跟着启动-->
<load-on-startup>1</load-on-startup>
</servlet>


<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--,所有的请求都需要通过DispatcherServlet -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 启用Spring的Session -->
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

</web-app>

发现报错:URI is not registered (Settings | Languages & Frameworks | Schemas and DTDs)

在idea中按照报错的步骤把http://xmlns.jcp.org/xml/ns/javaee 加入进去即可

  • 上方的配置文件,需要加载spring的配置文件,在resources下新建spring-mvc.xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫包,spring可以识别此包以及子包下的类中的spring相关注解,比如Service,Controller,Component等-->
<context:component-scan base-package="xyz.shi" />
<!-- 配置@controller扫描包 -->
<!--<context:component-scan base-package="cn.book.controller" />-->
<context:annotation-config/>
<!-- 配置注解驱动,相当于同时使用最新处理器映射跟处理器适配器,对json数据响应提供支持 -->
<mvc:annotation-driven enable-matrix-variables="true"/>
<!--对webapp下的静态文件放行,不去走中央控制器-->
<mvc:default-servlet-handler />

<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&amp;allowMultiQueries=true&amp;serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 配置jdbc -->

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务管理器 -->
<!-- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">-->
<!-- <property name="dataSource" ref="dataSource"/>-->
<!-- </bean>-->
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/users/**"/>
<bean class="xyz.shi.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

</beans>
  • dao层
1
2
3
4
5
6
7
8
9
public interface UserDao {
boolean login(User user);
void save(User user);
void delete(int id);
void update(User user);
User findById(int id);
List<User> findAll();
}

  • 实现dao,这里就是和数据库直接对接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public boolean login(User user) {
String sql = "SELECT * FROM users WHERE name = ? AND password = ?";
List<User> users = jdbcTemplate.query(sql, new Object[]{user.getName(), user.getPassword()},
BeanPropertyRowMapper.newInstance(User.class));
if (!users.isEmpty()) {
return true;
} else {
return false;
}
}

@Override
public void save(User user) {
String sql = "INSERT INTO users (name, password) VALUES (?, ?)";
jdbcTemplate.update(sql, user.getName(), user.getPassword());
}

@Override
public void delete(int id) {
String sql = "DELETE FROM users WHERE id = ?";
jdbcTemplate.update(sql, id);
}

@Override
public void update(User user) {
String sql ="update `users` set name = ?,password = ? where id = ? ";
jdbcTemplate.update(sql, user.getName(), user.getPassword(), user.getId());
}

@Override
public User findById(int id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{id},
BeanPropertyRowMapper.newInstance(User.class));
}

@Override
public List<User> findAll() {
String sql = "SELECT * FROM users";
// 查询数据
List<User> userList = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(User.class));
return userList;
}
}
  • service层
1
2
3
4
5
6
7
8
public interface UserService {
boolean login(User user);
void save(User user);
void delete(int id);
void update(User user);
User findById(int id);
List<User> findAll();
}
  • 实现service,这里主要作用实现service层的接口,具体数据逻辑是调用的userdao层,而userdao自动关联到了他的实现层也就是UserDaoImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public boolean login(User user) {
return userDao.login(user);
}
@Override
public void save(User user) {
userDao.save(user);
}
@Override
public void delete(int id) {
userDao.delete(id);
}
@Override
public void update(User user) {
userDao.update(user);
}
@Override
public User findById(int id) {
return userDao.findById(id);
}
@Override
public List<User> findAll() {
return userDao.findAll();
}
}
  • Controller层的登录 主要用来处理登录和注销
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

@Controller
@SessionAttributes("username")
@RequestMapping("/home")
public class LoginController {
@Autowired
private UserDao userDao;

@GetMapping("/userlogin")
public String userlogin() {
return "userlogin";
}

@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password,Model model,HttpServletRequest request) {
User user1 = new User();
user1.setName(username);
user1.setPassword(password);
boolean flag = userDao.login(user1);
if (!flag) {
model.addAttribute("error", "用户名或密码错误");
System.out.println("用户名或密码错误");
// 页面路径
// return "/WEB-INF/view/user/add.jsp";
return "userlogin";
}
// model.addAttribute("username", username);
//将用户信息放入 session 中
request.getSession(true).setAttribute("username",username);
System.out.println("登录成功"+ username);

return "redirect:/users/list";
}

@GetMapping("/logout")
public String logout(HttpSession session,SessionStatus sessionStatus) {
session.removeAttribute("username");
sessionStatus.setComplete();
System.out.println("注销成功");
System.out.println(session.getAttribute("username"));
return "redirect:userlogin";
}
}
  • Controller层的UserController 主要用来处理登录后对用户进行增删改查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

@Controller
@RequestMapping("/users")
public class UserController {
@Autowired
private UserDao userDao;


@GetMapping("/list")
public String list(Model model) {

List<User> users = userDao.findAll();
model.addAttribute("users", users);
return "userList";
}
@GetMapping("/addPage")
public String addPage(Model model) {
return "useradd";
}
@PostMapping("/add")
public String add(@RequestParam String username, @RequestParam String password, Model model) {
User user = new User();
user.setName(username);
user.setPassword(password);
userDao.save(user);
System.out.println("添加用户信息成功");
return "redirect:/users/list";
}
@GetMapping("/findPage")
public String findPage(@RequestParam Integer id ,Model model) {
User user = userDao.findById(id);
if (user!=null) {
model.addAttribute("user", user);
} else {
System.out.println("用户信息不存在");
}
return "useredit";
}
@PostMapping("/update")
public String update(@RequestParam Integer id ,@RequestParam String username, @RequestParam String password) {
User user = new User();
user.setId(id);
user.setPassword(password);
user.setName(username);
userDao.update(user);
System.out.println("更新成功");
return "redirect:/users/list";
}
@GetMapping("/del")
public String del(@RequestParam Integer id) {
userDao.delete(id);
System.out.println("删除成功");
return "redirect:/users/list";

}
  • LoginInterceptor 主要是对没有登录的进行拦截,自动跳转到登录界面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package xyz.shi.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession();
// 检查用户是否已登录,如果未登录则跳转到登录页
if (session.getAttribute("username") == null) {
response.sendRedirect(request.getContextPath() + "/home/userlogin");
return false;
} else {
return true;
}
}
}
  • jsp界面,路径在/WEB-INF/jsp/目录下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<form action="${pageContext.request.contextPath}/home/login" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required><br><br>
<input type="submit" value="登录">
</form>
</body>
</html>
  • 本地安装tomcat,我的版本为9.0.83,然后idea中装插件Smart tomcat,然后在webapp上右键运行

  • 测试页面

image-20240219174029931

说明

hibernate

一对一

  • 配置基本不变
  • 实体类,一个人对应一个信用卡
1
2
3
4
5
6
7
8
9
10
package xyz.shi.domain;
import javax.persistence.*;
@Entity
@Table(name = "t_idcard")
public class IdCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String cardNo;
.....
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ckage xyz.shi.domain;

import javax.persistence.*;

@Entity
@Table(name = "t_person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
// 单向关联只要向子表加入关系
@OneToOne
@JoinColumn(name = "t_idCard_id")
private IdCard idCard;
...

只做了单向关联

  • dao
1
2
3
4
5
6
7
8
9
10
11
12
13
package xyz.shi.dao;

import xyz.shi.domain.QueryResult;
import xyz.shi.domain.Person;

public interface PersonDao {
void save(Person person);
void update(Person person);
Person findById(int id);
QueryResult findAll(int firstResult, int maxResults);
void delete(Person person);
}

  • 实现dao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package xyz.shi.dao.Impl;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.Person;
import xyz.shi.domain.QueryResult;
import xyz.shi.domain.User;
import java.util.List;

@Repository
public class PersonDaoImpl implements PersonDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public void save(Person person) {
Session session = sessionFactory.getCurrentSession();
session.save(person);

}

@Override
public void update(Person person) {
Session session = sessionFactory.getCurrentSession();
session.update(person);
}

@Override
public Person findById(int id) {
Session session = sessionFactory.getCurrentSession();
Person person = session.get(Person.class, id);
return person;
}
@Override
public QueryResult findAll(int firstResult, int maxResults) {
Session session = sessionFactory.getCurrentSession();

List<User> list = session.createQuery(
"FROM Person")
.setFirstResult(firstResult)
.setMaxResults(maxResults)
.list();
Long count = (Long)session.createQuery( //
"SELECT COUNT(*) FROM User") //
.uniqueResult();
return new QueryResult(count.intValue(), list);
}

@Override
public void delete(Person person) {
Session session = sessionFactory.getCurrentSession();
session.delete(person);
}
}

  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.IdCard;
import xyz.shi.domain.Person;
import xyz.shi.domain.QueryResult;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class PersonTest {
@Autowired
private PersonDao dao;

@Test
@Transactional
// @Rollback(false) // 不需要回滚
public void save() {
IdCard idCard = new IdCard();
idCard.setId(17);
Person person = new Person();
person.setName("王大伟7");
person.setIdCard(idCard);
dao.save(person);
}
@Test
@Transactional
// @Rollback(false)
public void find() {
QueryResult result = dao.findAll(0, 5);
System.out.println(result.getCount());
for (Object o : result.getList()) {
Person person = (Person) o;
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}

}
@Test
@Transactional
@Rollback(false)
public void update() {
IdCard idCard = new IdCard();
idCard.setId(17);
Person person = new Person();
person.setName("王大伟16");
person.setIdCard(idCard);
person.setId(16);
dao.update(person);
}
}

一对多

  • 准备两个实体类,一个班级下有多个学生
  • 班级实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package xyz.shi.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "grade")
public class Grade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer Id;
private String name;
//持有 Student 引用的集合,来维护一对多关联关系
@OneToMany(mappedBy = "grade", cascade = CascadeType.ALL)
private Set<Student> students = new HashSet<>();
...
  • 学生实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
//持有实体类 Grade 的一个引用,维护多对一关系
// 多对一关联关系
@ManyToOne
@JoinColumn(name = "grade")
private Grade grade;
....
  • dao
1
2
3
4
public interface StudentDao {
void save(Student student);
QueryResult findAll(int firstResult, int maxResults);
}
  • 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

@Repository
public class StudentDaoImpl implements StudentDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public void save(Student student) {
Session session = sessionFactory.getCurrentSession();
session.save(student);
}

@Override
public QueryResult findAll(int firstResult, int maxResults) {
Session session = sessionFactory.getCurrentSession();
List<Object[]> list = session.createQuery( //
"select s, g from Student s join s.grade g") //
.setFirstResult(firstResult) //
.setMaxResults(maxResults) //
.list();

// 查询总记录数
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM Student") //
.uniqueResult();
return new QueryResult(count.intValue(), list);
}
}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class StudentTest {
@Autowired
private StudentDao studentDao;

@Test
@Transactional
// @Rollback(false) // 不需要回滚
public void save() {
Grade grade = new Grade();
grade.setId(36);

Student student = new Student();
student.setName("王大伟36");
//设置学生的班级
student.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红36");

studentDao.save(student);
studentDao.save(student2);
}
@Test
@Transactional
public void findAll() {
QueryResult result = studentDao.findAll(0, 5);
System.out.println(result.getCount());
List<Object[]> list = result.getList();
for (Object[] arr : list) {
Student student = (Student) arr[0];
System.out.println("Student: " + student.getName() + ", Grade: " + student.getGrade().getName());

}
}

多对多

  • 多个学生和多个课程
  • 学生实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
//持有实体类 Grade 的一个引用,维护多对一关系
// 多对一关联关系
@ManyToOne
@JoinColumn(name = "gid")
private Grade grade;
//将 Course 对象的集合作为其属性,以维护它们之间的多对多关联关系
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();

@JoinColumn(name = “gid”) 这里的name对应数据表中的字段,若填的字段在数据表中不存在,就会自动创建

  • 课程类
1
2
3
4
5
6
7
8
9
10
@Entity
@Table(name = "course")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
//学生 Student 的集合作为其属性,维护多对多关联关系
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();

​ @ManyToMany(mappedBy = “courses”) 这里的courses对应Student实体类中的courses

  • dao
1
2
3
4
public interface StudentManyToManyDao {
void save(Student student);
QueryResult findAll(int firstResult, int maxResults);
}
  • 实现dao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

@Repository
public class StudentManyToManyDaoImpl implements StudentManyToManyDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public void save(Student student) {
Session session = sessionFactory.getCurrentSession();
session.save(student);
}

@Override
public QueryResult findAll(int firstResult, int maxResults) {
Session session = sessionFactory.getCurrentSession();
String hql = "SELECT s, c, g FROM Student s " +
"JOIN s.courses c " +
"JOIN s.grade g";
List<Object[]> list = session.createQuery(hql)
.setFirstResult(firstResult)
.setMaxResults(maxResults)
.list();

// 查询总记录数
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM Student") //
.uniqueResult();
return new QueryResult(count.intValue(), list);
}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class StudentManyToManyTest {
@Autowired
private StudentManyToManyDao studentDao;
@Test
@Transactional
@Rollback(false) // 不需要回滚
public void save() {
//-----新增两个学生,关联到一个班级下面,同时新增的两个学生关联不同的课程(需要用到第三方表)

//一个班级
Grade grade = new Grade();
grade.setId(36);
// 新建两个学生
Student student1 = new Student();
student1.setName("王大伟36121");
//设置学生的班级
student1.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红36121");
// 两个学生各自对应两个课程
Course course1 = new Course();
course1.setId(17);

Course course2 = new Course();
course2.setId(18);


studentDao.save(student1);
studentDao.save(student2);

//学生选课的关系相互关联
course1.getStudents().add(student1);
student1.getCourses().add(course1);

course2.getStudents().add(student2);
student2.getCourses().add(course2);
}
@Test
@Transactional
public void findAll() {
QueryResult qresult = studentDao.findAll(0, 5);
System.out.println(qresult.getCount());
List<Object[]> list = qresult.getList();
for (Object[] result : list) {
Student student = (Student) result[0];
Course course = (Course) result[1];
Grade grade = (Grade) result[2];
System.out.println(student);
System.out.println(course);
System.out.println(grade);
}
}
}

Jpa

  • 配置不变

一对一

  • 一个人对应一个信用卡号
  • Person实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package xyz.shi.domain;
import javax.persistence.*;
@Entity
@Table(name = "t_person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

private String name;
// 单向关联只要向子表加入关系
@OneToOne
// @OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name = "t_idCard_id")
private IdCard idCard;
  • IdCard实体类
1
2
3
4
5
6
7
8
import javax.persistence.*;
@Entity
@Table(name = "t_idcard")
public class IdCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String cardNo;
  • personDao
1
2
3
4
5
6
7
package xyz.shi.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import xyz.shi.domain.Person;
public interface PersonDao extends JpaRepository<Person, Integer>, JpaSpecificationExecutor<Person> {
}

  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.IdCard;
import xyz.shi.domain.Person;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class PersonTest {
@Autowired
PersonDao dao;
@Test
public void testFindPage() {
PageRequest page = PageRequest.of(0, 5, Sort.by(Sort.Order.desc("id")));
Page<Person> all = dao.findAll(page);
System.out.println("分页 + 根据id逆序 查询结果: " + all.getContent());
for(Person person: all) {
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
}
@Test
public void testFindBy() {
Person person = dao.findById(37).get();
System.out.println(person.getName() + "_"+ person.getIdCard().getCardNo());
}
@Test
public void save() {
IdCard idCard = new IdCard();
idCard.setId(14);
Person person = new Person();
person.setName("tttt");
person.setIdCard(idCard);
dao.save(person);
}
@Test
public void update() {
IdCard idCard = new IdCard();
idCard.setId(17);
Person person = new Person();
person.setName("tttt2");
person.setIdCard(idCard);
person.setId(38);
dao.save(person);
}
@Test
public void delete() {
dao.deleteById(37);
}
}

一对多 多对多

  • 一个班级下有多个学生
  • 班级实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package xyz.shi.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "grade")
public class Grade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer Id;
private String name;
//持有 Student 引用的集合,来维护一对多关联关系
@OneToMany(mappedBy = "grade", cascade = CascadeType.ALL)
private Set<Student> students = new HashSet<>();
...
  • 学生实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package xyz.shi.domain;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
//持有实体类 Grade 的一个引用,维护多对一关系
// 多对一关联关系
@ManyToOne
@JoinColumn(name = "gid")
private Grade grade;
//将 Course 对象的集合作为其属性,以维护它们之间的多对多关联关系
@ManyToMany(targetEntity = Course.class)
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
....
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.shi.dao.StudentDao;
import xyz.shi.domain.Course;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;

import java.util.ArrayList;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class StudentTest {
@Autowired
private StudentDao dao;
@Test
public void oneToMany() {
PageRequest page = PageRequest.of(0, 5, Sort.by(Sort.Order.desc("id")));
Page<Student> all = dao.findAll(page);
System.out.println("分页 + 根据id逆序 查询结果: " + all.getContent());
for(Student student: all) {
System.out.println(student.getName());
System.out.println(student.getGrade().getName());
}
Grade grade = new Grade();
grade.setId(36);

Student student = new Student();
student.setName("王大伟4");
//设置学生的班级
student.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红5");

List<Student> studentList = new ArrayList<>();
studentList.add(student);
studentList.add(student2);

dao.saveAll(studentList);
// dao.deleteAll();
}
@Test
public void ManyToManyFind() {
Pageable pageable = PageRequest.of(0, 5);
Page<Object[]> page = dao.findAllCourse(pageable);

System.out.println("Page " + page.getNumber() + " of " + page.getTotalPages());
System.out.println("Total elements: " + page.getTotalElements());

for(Object[] objects: page.getContent()) {
Student student = (Student) objects[0];
Course course = (Course) objects[1];
Grade grade = (Grade) objects[2];
System.out.println(student.getName());
System.out.println(course.getName());
System.out.println(grade.getName());
}
}
@Test
public void ManyToManySave() {
//-----新增两个学生,关联到一个班级下面,同时新增的两个学生关联不同的课程(需要用到第三方表)

//一个班级
Grade grade = new Grade();
grade.setId(36);
// 新建两个学生
Student student1 = new Student();
student1.setName("王大伟7");
//设置学生的班级
student1.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红71");
// 两个学生各自对应两个课程
Course course1 = new Course();
course1.setId(17);

Course course2 = new Course();
course2.setId(18);

//学生选课的关系相互关联
course1.getStudents().add(student1);
student1.getCourses().add(course1);

course2.getStudents().add(student2);
student2.getCourses().add(course2);

dao.save(student1);
dao.save(student2);
}
@Test
public void ManyToManyUpdate() {
//一个班级
Grade grade = new Grade();
grade.setId(36);


// 新建两个学生
Student student1 = new Student();
student1.setName("王大伟7");
student1.setId(43);
//设置学生的班级
student1.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红71");
student2.setId(45);
// 两个学生各自对应两个课程
Course course1 = new Course();
course1.setId(17);

Course course2 = new Course();
course2.setId(18);

//学生选课的关系相互关联

// 先移除关联关系
student1.getCourses().remove(course1);
course1.getStudents().remove(student1);

student2.getCourses().remove(course2);
course2.getStudents().remove(student2);
// 更新关联关系
course1.getStudents().add(student1);
student1.getCourses().add(course1);

course2.getStudents().add(student2);
student2.getCourses().add(course2);

dao.save(student1);
dao.save(student2);
}
@Test
public void ManyToManyDel() {

Student student = new Student();
student.setId(46);

Course course = new Course();
course.setId(39);

// 移除关联关系
student.getCourses().remove(course);
course.getStudents().remove(student);

dao.delete(student);

}
}

Mybatis

一对一

  • 实体类就沿用IdCard和Person

  • 配置需要加入对应的mapper文件

  • mybatis.xml

1
2
3
4
5
6
!-- sql映射文件的位置 -->
<mappers>
<!-- 注册userMapper.xml文件 -->
<mapper resource="mapper/UserMapper.xml"/>
<mapper resource="mapper/PersonMapper.xml"/>
</mappers>
  • application.xml
1
2
3
4
5
6
7
8
9
<!--下面的就是自己定义的service-->
<!--声明service-->
<bean id="userService" class="xyz.shi.service.Impl.UserServiceImpl">
<!--就是上面通过创建的dao对象-->
<property name="userDao" ref="userDao"/>
</bean>
<bean id="personService" class="xyz.shi.service.Impl.PersonServiceImpl">
<property name="personDao" ref="personDao"/>
</bean>
  • PersonMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?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="xyz.shi.dao.PersonDao">
<!-- 定义封装person和IdCard的resultMap -->
<resultMap id="personIdCardMap" type="xyz.shi.entity.Person">
<id property="id" column="id"/>
<!--IdCard的id -->
<result property="name" column="name"/>
<!-- association配置一对一关系,property为Person中属性名,javaType为属性类型 -->
<association property="idCard" javaType="xyz.shi.entity.IdCard">
<!-- column="t_idCard_id 对应person表中的t_idCard_id-->
<id property="id" column="t_idCard_id"/>
<result property="cardNo" column="cardNo"/>
</association>
</resultMap>
<!--配置查询所有-->
<select id="findAll" resultMap="personIdCardMap">
select p.name, p.id as pid, c.id as cid, c.cardNo from t_person p, t_idCard c where c.id = p.t_idCard_id
</select>
<!-- 通过id(唯一)获取用户-->
<select id="queryOne" parameterType="int" resultMap="personIdCardMap">
select p.name, p.id as pid, c.id as cid, c.cardNo from t_person p, t_idCard c where c.id = p.t_idCard_id and p.id=#{id}

</select>
<!-- 插入 -->
<insert id="insert" parameterType="Person">
insert into t_person (name,t_idCard_id) values(#{name},#{idCard.id})
</insert>

<update id="modify" parameterType="Person">
update t_person set name=#{name},t_idCard_id=#{idCard.id} where id=#{id}
</update>
<!-- 根据 id 删除 -->
<delete id="delete" parameterType="int">
delete from t_person where id=#{id}
</delete>
</mapper>

association配置一对一关系,其实就是把之前练习的学习Mybatis的内容移过来

  • dao
1
2
3
4
5
6
7
public interface PersonDao {
Page<Person> findAll();
Person queryOne(int id);
int insert(Person person);
int modify(Person person);
int delete(int id);
}
  • service
1
2
3
4
5
6
7
public interface PersonService {
Page<Person> findAll();
Person queryOne(int id);
int insert(Person person);
int modify(Person person);
int delete(int id);
}
  • 实现service
1
2
3
4
5
6
7
8
9
10
11
12
13

public class PersonServiceImpl implements PersonService {
// 引用类型
private PersonDao personDao;
// 为了使用set注入来赋值,一定要加这个
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public Page<Person> findAll() {
return personDao.findAll();
....
}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class PersonTest {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
// 其实就是调用的app..xml中的<bean id="userService" class="xyz.shi.service.Impl.UserServiceImpl">
PersonService personService = (PersonService) context.getBean("personService");

@Test
public void TestFindAll() {
//在查询之前,设置分页条件 显示第一页,展示3条数据
Page<Object> page = PageHelper.startPage(1, 3);
List<Person> personList = personService.findAll();
// 设置分页导航数量5
PageInfo<Person> pageInfo = new PageInfo<>(personList, 5);
for (Person person:personList) {
System.out.println("------每个用户的信息-------");
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}
@Test
public void TestQueryOne() {
Person person = personService.queryOne(16);
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
@Test
public void TestAddPerson() {
Person person = new Person();
person.setName("山大王");

IdCard idCard = new IdCard();
idCard.setCardNo("222");
idCard.setId(14);

person.setIdCard(idCard);

personService.insert(person);
}

@Test
public void TestModifyPerson() {
Person person = new Person();
person.setName("山大王234");
person.setId(31);

IdCard idCard = new IdCard();
idCard.setId(17);

person.setIdCard(idCard);
personService.modify(person);
}

@Test
public void TestDelete() {
personService.delete(31);
}
}
  • 一对多和多对多,配置基本一样唯一区别就是在mapper.xml使用association进行关联,他们的测试类分别为:GradeTest(一对多),StudentTest(多对多)

  • 源代码

MybatisPlus

  • mybatis-plus在连表查询上是不行的,如果需要连表查询,那么我们就得乖乖的去写xml文件了
  • 推荐用一个插件,官网在这里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class test {
@Resource
private UserMapper userMapper;

@Test
void testResultMap() {
MPJLambdaWrapper<UserDO> wrapper = new MPJLambdaWrapper<>(User.class)
.selectAll(UserDO.class)
//对多查询
.selectCollection(AddressDO.class, UesrDTO::getAddressList)
//对一查询
.selectAssociation(AddressDO.class, UesrDTO::getAddress)
.leftJoin(AddressDO.class, AddressDO::getUserId, UserDO::getId);

List<UserDTO> dtoList = userMapper.selectJoinList(UserDTO.class, wrapper);

//关于对多分页查询
//由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。
}
}

说明

  • 上篇文章主要介绍spring如何整合jdbc、mybatis,本文章继续整合orm中的常用框架

  • 环境:idea社区版本、jdk1.8,mysql8.0+,maven,win10

整合hibernate

  • 依赖文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<!-- 事务 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.7.Final</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
</dependencies>
  • src-main-resources-db.properties
1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai
user=root
password=123456
  • src-main-resources-applicationContext.xml 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 引入db.properties文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 创建druid 的数据源 -->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="url" value="${url}" />
<property name="driverClassName" value="${driver}" />
<property name="username" value="${user}" />
<property name="password" value="${password}" />
</bean>
<!-- 配置Hibernate的SessionFactory对象 -->
<bean class="org.springframework.orm.hibernate5.LocalSessionFactoryBean" id="sessionFactory">
<!-- 关联数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置Hibernate的属性信息 -->
<property name="hibernateProperties">
<props>
<prop key="show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!-- 扫描路径 -->
<property name="packagesToScan">
<list>
<value>xyz.shi.domain</value>
</list>
</property>
</bean>
<!-- 配置HibernateTemplate对象 -->
<bean class="org.springframework.orm.hibernate5.HibernateTemplate" id="hibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 配置事务管理-->
<bean class="org.springframework.orm.hibernate5.HibernateTransactionManager" id="transactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- 配置扫描路径 -->
<context:component-scan base-package="xyz.shi" />
</beans>
  • userdao
1
2
3
4
5
6
7
8
9
10
11
12
13
package xyz.shi.dao;

import xyz.shi.domain.QueryResult;
import xyz.shi.domain.User;

public interface UserDao {
void save(User user);
void update(User user);
User findById(int id);
QueryResult findAll(int firstResult, int maxResults);
void delete(User user);
}

  • 实现userdao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package xyz.shi.dao.Impl;

import org.hibernate.SessionFactory;
import xyz.shi.dao.UserDao;
import xyz.shi.domain.QueryResult;
import xyz.shi.domain.User;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private SessionFactory sessionFactory;
// private HibernateTemplate template;
@Override
public void save(User user) {
Session session = sessionFactory.getCurrentSession();
session.save(user);

}

@Override
public void update(User user) {
Session session = sessionFactory.getCurrentSession();
session.update(user);

}

@Override
public User findById(int id) {
Session session = sessionFactory.getCurrentSession();
User user = session.get(User.class, id);
return user;
}


/**
* 分页的查询数据列表
* @param firstResult 从结果列表中的哪个索引开始取数据
* @param maxResults 最多取多少条数据
* @return 一页的数据列表
*/
@Override
public QueryResult findAll(int firstResult, int maxResults) {
Session session = sessionFactory.getCurrentSession();

List<User> list = session.createQuery(
"FROM User")
.setFirstResult(firstResult)
.setMaxResults(maxResults)
.list();
Long count = (Long)session.createQuery(
"SELECT COUNT(*) FROM User")
.uniqueResult();
return new QueryResult(count.intValue(), list);
}

@Override
public void delete(User user) {
Session session = sessionFactory.getCurrentSession();
session.delete(user);

}
}

  • 分页实体类
1
2
3
4
5
public class QueryResult {

private int count; // 总记录数
private List list; // 一页的数据
...
  • user实体类 省略

  • 开始测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import xyz.shi.dao.UserDao;
import xyz.shi.domain.QueryResult;
import xyz.shi.domain.User;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserTest {
@Autowired
private UserDao dao;

@Test
@Transactional
@Rollback(false) // 不需要回滚
public void save() {
User user = new User();
user.setUserName("test789");
user.setPassword("777777");
dao.save(user);
}
@Test
@Transactional
// 如果不加这个,以上就是回滚,这样的好处就是在单元测试中能测试通过,但是最终不修改数据库的值
@Rollback(false)
public void update() {
User user = new User();
user.setUserId(52);
user.setUserName("test8888");
user.setPassword("888888");
dao.update(user);
}
@Test
@Transactional
public void findId() {
User user = dao.findById(52);
System.out.println(user.getUserName());
}
@Test
@Transactional
public void findAll() {
QueryResult result = dao.findAll(0, 5);
System.out.println(result.getCount());
for (Object o : result.getList()) {
User user = (User)o;
System.out.println(user.getUserName());
}
}
@Test
@Transactional
public void delete() {
User user = new User();
user.setUserId(53);
dao.delete(user);
}
}

注意这里的标识

Transactional 开启事务

Rollback(false) 这样的好处就是在单元测试中能测试通过,但是最终不修改数据库的值,在发布到生产环境需要检查,切记需要去掉此标识,不然造成修改数据全部都回滚了,造成数据修改不成功

jpa

  • jpa是基于hibernate来实现的

  • 依赖文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    <dependencies>
<!-- Spring框架核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>
<!-- Spring JPA依赖 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.5.4</version>
</dependency>
<!-- Hibernate JPA实现依赖 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.27.Final</version>
</dependency>

<!-- MySQL连接驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
  • 实体类,映射数据库中表的关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package xyz.shi.domain;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name="users")
public class User implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Integer userId;
@Column(name="name")
private String userName;
@Column(name="password")
private String password;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}

}
  • src-main-java-resources-applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

<context:annotation-config />

<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>xyz.shi.domain</value> <!-- 替换成你的实体类所在包名 -->
</list>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</prop> <!-- 替换成你所使用的MySQL版本对应的方言类 -->
<prop key="hibernate.hbm2ddl.auto">update</prop> <!-- 自动更新表结构 -->
</props>
</property>
</bean>

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" /> <!-- 替换成你所使用的MySQL版本对应的驱动类 -->
<property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai" /> <!-- 替换成你的数据库连接信息 -->
<property name="username" value="root" /> <!-- 替换成你的数据库用户名 -->
<property name="password" value="123456" /> <!-- 替换成你的数据库密码 -->
</bean>

<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<jpa:repositories base-package="xyz.shi.dao" /> <!-- 替换成你的Repository接口所在包名 -->

</beans>

  • dao层,几乎不用写任何逻辑,当然页支持自定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package xyz.shi.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import xyz.shi.domain.User;



public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
// 普通的查询方法
// 示例 getByName(String name):getBy固定用法
//1. Name是User成员变量的首字母大写
//2. name与User的成员变量name相同
// public User getByUserName(String name);
// @Query("SELECT u FROM User u WHERE u.name LIKE %:name%")
// List<User> findByNameContaining(@Param("name") String name);
}

  • 测试src-test-java-UserTest1.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.shi.dao.UserDao;
import xyz.shi.domain.User;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")

public class UserTest1 {
@Resource
UserDao userDao;
@Test
public void testFindPage() {

PageRequest page = PageRequest.of(0, 5, Sort.by(Sort.Order.desc("userId")));
Page<User> all = userDao.findAll(page);
System.out.println("分页 + 根据id逆序 查询结果: " + all.getContent());
}
@Test
public void testFindBy() {
User user2 = userDao.findById(50).get();
System.out.println(user2.getUserName());
}
@Test
public void save() {
User user = new User();
user.setUserName("tttt");
user.setPassword("pppp");
User user11 = userDao.save(user);
System.out.println(user11.getUserId());
}
@Test
public void update() {
User user = new User();
user.setUserName("tttt60");
user.setPassword("pppp60");
user.setUserId(60);
userDao.save(user);
}
@Test
public void delete() {
User user = new User();
user.setUserId(59);
userDao.delete(user);
}
}

mybatisplus

  • 这篇文章练习了spring集成mybatis,本次用mybatisplus

  • java8,mysql8,idea社区版,win10

  • 依赖文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
</dependencies>
  • 实体类,User
1
2
3
4
5
6
7
8
9
10
11
12
13
package xyz.shi.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("users")

public class User {
@TableId
private int id;
private String name;
private String password;
}
  • UserMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package xyz.shi.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xyz.shi.entity.User;

@Mapper
// BaseMapper中包含了常用的增删改查的方法,也可以自定义方法
public interface UserMapper extends BaseMapper<User> {
// @Select("SELECT * FROM users WHERE id = #{id}")
// User getUserById(int id);
//
// @Insert("INSERT INTO users(name,) VALUES(#{name}})")
// int insertUser(User user);
//
//// @Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
// @Update("UPDATE user SET name = #{name} WHERE id = #{id}")
// int updateUser(User user);
//
// @Select("SELECT * FROM users WHERE name = #{name}")
// List<User> getUsersByName(String name);
}
  • src-main-resources-appcationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 加载数据库配置文件 -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&amp;serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="autoCommit" value="true"/>
<property name="connectionTimeout" value="5000" />
<property name="idleTimeout" value="60" />
</bean>
<!--jdbc的xml配置-->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<constructor-arg name="configuration" ref="hikariConfig"/>
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 配置MapperScannerConfigurer -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="xyz.shi.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
</beans>
  • src-main-resources-mybatis-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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:控制mybatis全局行为-->
<settings>
<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<typeAliases>
<!--
package:把包下面的所有类名作为别名
name:实体类所在的包名-->
<package name="xyz.shi.entity"/>
</typeAliases>
</configuration>
  • 单元测试,src-test-java-UserMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.shi.entity.User;
import xyz.shi.mapper.UserMapper;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserMapperTest {
@Autowired
UserMapper userDao;
@Test
public void selectById() {
User user = userDao.selectById(50);
System.out.println(user.getName());
}
@Test
public void findPage() {
Page<User> page = new Page<>(1, 10);
List<User> userList = userDao.selectPage(page, null).getRecords();
for(User user1: userList) {
System.out.println(user1.getName());
}
}
@Test
public void save() {
User user = new User();
user.setName("mybatisplus");
user.setPassword("11111");
userDao.insert(user);
}
@Test
public void update() {
User user = new User();
user.setId(61);
user.setName("tst60");
user.setPassword("2222");
userDao.updateById(user);
}
@Test
public void delete() {
userDao.deleteById(61);
}
}

  • 注意本次没有用mapper.xml文件,完全抛弃了
  • 源代码

说明

  • 本地环境:mysql 8.0,java 1.8,idea社区版本
  • 这篇文章 介绍了对Hibernate的关系映射的使用
  • 之前写过jsp+mybatis的文章,本次详细介绍mybatis的常见用法

依赖文件

  • pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

一对一

  • 这篇文章 介绍了对Hibernate的关系映射

  • 直接用表t_idCard、t_person

  • 直接用Person、IdCard实体类

  • 创建db.properties文件,在resources目录下

1
2
3
4
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai
user=root
password=123456
  • 接下来就要创建一个xml文件作为MyBatis的核心配置文件了,src-main-resources-mybatis-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!--mybatis-config.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>

<!--读取配置文件中的数据库连接信息-->
<properties resource="db.properties"/>
<settings>
<!--设置日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启驼峰命名,映射数据库到对象属性名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>

<!--给实体类取别名,为了后面配置mapper.xml时不用写全限定名,方便使用-->
<typeAliases>
<!--包扫描-->
<package name="xyz.shi.entity"/>
</typeAliases>

<environments default="development">
<!--id属性必须和上面的default一样 -->
<environment id="development">
<transactionManager type="JDBC"/>
<!--dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象源 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 注册userMapper.xml文件 -->
<mapper resource="UserMapper.xml"/>
</mappers>

</configuration>
  • src-main-resources-personMapper.xml 新建如下person表的增删改查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?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="personMapper">
<!-- 定义封装person和IdCard的resultMap -->
<resultMap id="personIdCardMap" type="xyz.shi.domain.Person">
<id property="id" column="id"/>
<!--IdCard的id -->
<!-- <result property="t_idCard_id" column="t_idCard_id"/>-->
<result property="name" column="name"/>
<!-- association配置一对一关系,property为Person中属性名,javaType为属性类型 -->
<association property="idCard" javaType="xyz.shi.domain.IdCard">
<!-- column="t_idCard_id 对应person表中的t_idCard_id-->
<id property="id" column="t_idCard_id"/>
<result property="cardNo" column="cardNo"/>
</association>
</resultMap>
<!--配置查询所有-->
<select id="findAll" resultMap="personIdCardMap">
select p.name, p.id as pid, c.id as cid, c.cardNo from t_person p, t_idCard c where c.id = p.t_idCard_id
</select>
<!-- 通过id(唯一)获取用户-->
<select id="queryOne" parameterType="int" resultMap="personIdCardMap">
select p.name, p.id as pid, c.id as cid, c.cardNo from t_person p, t_idCard c where c.id = p.t_idCard_id and p.id=#{id}

</select>
<!-- 插入 -->
<insert id="insertPerson" parameterType="Person">
insert into t_person (name,t_idCard_id) values(#{name},#{idCard.id})
</insert>

<update id="modifyPerson" parameterType="Person">
update t_person set name=#{name},t_idCard_id=#{idCard.id} where id=#{id}
</update>
<!-- 根据 id 删除 -->
<delete id="deletePerson" parameterType="int">
delete from t_person where id=#{id}
</delete>
</mapper>
  • personDao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package xyz.shi.dao;

import xyz.shi.domain.Person;

import java.util.List;

public interface PersonDao {
List<Person> findAll();
Person queryOne(int id);
int insert(Person person);
int modify(Person person);
int delete(int id);
}

  • 实现personDao接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package xyz.shi.dao.impl;

import org.apache.ibatis.session.SqlSession;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.Person;
import xyz.shi.utils.MybatisUtils;
import java.util.List;

public class PersonDaoImpl implements PersonDao {
@Override
public List<Person> findAll() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 命名空间+id,这就是命名空间的作用:便于区分
List<Person> personList = sqlSession.selectList("personMapper.findAll");
sqlSession.close();
return personList;
}

@Override
public Person queryOne(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
Person person = sqlSession.selectOne("personMapper.queryOne", id);
sqlSession.close();
return person;

}

@Override
public int insert(Person person) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("personMapper.insertPerson", person);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int modify(Person person) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("personMapper.modifyPerson", person);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int delete(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.delete("personMapper.deletePerson", id);
sqlSession.commit();
sqlSession.close();
return num;
}

}

  • sqlsession代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package xyz.shi.utils;

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 java.io.IOException;
import java.io.InputStream;

public class MybatisUtils {

//sqlSession工厂
private static SqlSessionFactory sqlSessionFactory;

//静态代码块,直接项目跑起来直接装载
static {
//设置要读取的资源路径
String resource = "mybatis.config.xml";
//声明一个输入流,方便后面读取
InputStream inputStream = null;

try {
//通过ibatis包下的资源读取的方法,通过提前设定好的路径将mybatis的核心配置文件读取进来,用来建造工厂
inputStream = Resources.getResourceAsStream(resource);
//new一个建造者,通过建造者创建一个带核心配置文件的工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

//通过工厂打开一个对数据库的会话
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}

  • 测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.junit.Test;
import xyz.shi.dao.PersonDao;
import xyz.shi.dao.impl.PersonDaoImpl;
import xyz.shi.domain.IdCard;
import xyz.shi.domain.Person;

import java.util.Arrays;
import java.util.List;

public class PersonDaoTest {
private PersonDao personDao = new PersonDaoImpl();
@Test
public void TestFindAll() {
//在查询之前,设置分页条件 显示第一页,展示3条数据
Page<Object> page = PageHelper.startPage(1, 3);

List<Person> personList = personDao.findAll();

PageInfo<Person> pageInfo = new PageInfo<>(personList, 5);

for (Person person:personList) {
System.out.println("------每个用户的信息-------");
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}
@Test
public void TestQueryOne() {
Person person = personDao.queryOne(16);
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
@Test
public void TestAddPerson() {
Person person = new Person();
person.setName("山大王");

IdCard idCard = new IdCard();
idCard.setCardNo("222");
idCard.setId(14);

person.setIdCard(idCard);

personDao.insert(person);
}

@Test
public void TestModifyPerson() {
Person person = new Person();
person.setName("山大王234");
person.setId(29);

IdCard idCard = new IdCard();
idCard.setId(17);

person.setIdCard(idCard);
personDao.modify(person);
}

@Test
public void TestDelete() {
personDao.delete(24);
}
}

一对多

  • 这篇文章 介绍了对Hibernate的关系映射

  • 直接用表student(多对一)、grade(一对多,一个年级下有多个学生)

  • 直接用Student、Grade实体类

  • src-main-resources-gradeMapper.xml 新建如下grade表的增删改查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?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="gradeMapper">
<!-- 定义封装resultMap
property 实体中对应的属性
column 数据库对应的列(select查询返回的列表)
-->
<resultMap id="gradeStudentMap" type="xyz.shi.domain.Grade">
<id property="id" column="gid"/>
<result property="name" column="gname"/>
<!--一对多关系的时候,一方使用collection标签映射多方 -->
<collection property="students" ofType="xyz.shi.domain.Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>

</collection>
</resultMap>
<!--配置查询所有,重复的列需要重命名-->
<select id="findAll" resultMap="gradeStudentMap">
select g.id as gid, g.name as gname, s.id as sid, s.name as sname from grade g left join student s on g.id = s.gid
</select>
<!-- 通过id(唯一)获取用户-->
<select id="queryOne" parameterType="int" resultMap="gradeStudentMap">
select s.name as sname,g.name as gname, g.id as gid, s.id as sid from grade g, student s where g.id = s.gid and g.id=#{id}
</select>
<!-- 插入 userGeneratedKeys和keyProperty属性可以让插入后对象获得本身自己的id
-->
<insert id="insertGrade" parameterType="Grade"
useGeneratedKeys="true" keyProperty="id">
insert into grade (name) values(#{name})
</insert>
<insert id="insertStudent" parameterType="xyz.shi.domain.Student">
INSERT INTO student (name, gid) VALUES (#{name}, #{grade.id})
</insert>
<!-- 批量新增的时候,注意接收的对象是一个列表,需要在动态拼接sql的时候遍历参数集合对象 -->
<insert id="insertBatchStudent" parameterType="java.util.List">
Insert into student (name,gid) values
<foreach collection="list" item="item" separator=",">
(#{item.name},#{item.grade.id})
</foreach>
</insert>
<update id="updateBatchStudent" parameterType="java.util.List">
<foreach collection="list" item="item" separator=";">
update student set name = #{item.name},gid = #{item.grade.id} where id = #{item.id}
</foreach>
</update>
<delete id="deleteBatchStudent" parameterType="java.util.List">
delete from student where id in
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
</mapper>

collection 维护一对多

注意foreach的用法

  • src-main-resources-studentMapper.xml 新建如下student表的查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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="studentMapper">
<!-- 定义封装resultMap
property 实体中对应的属性
column 数据库对应的列(select查询返回的列表)
-->
<resultMap type="xyz.shi.domain.Student" id="StudentGradeResult">
<id column="sid" jdbcType="BIGINT" property="id" />
<result column="sname" jdbcType="VARCHAR" property="name" />
<!-- 多对一关系的时候,多方使用association标签映射一方-->
<!-- <association property="grade" resultMap="StudentResult"></association>-->
<association property="grade" javaType="xyz.shi.domain.Grade">
<id column="gid" property="id" />
<result column="gname" property="name" />
</association>

</resultMap>
<select id="queryOne" parameterType="int" resultMap="StudentGradeResult">
select s.name as sname,g.name as gname, g.id as gid, s.id as sid from student s,grade g where g.id = s.gid and s.id=#{id}
</select>
</mapper>

association 维护多对一

  • 编写班级grade的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package xyz.shi.dao;

import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;

import java.util.List;

public interface GradeDao {
List<Grade> findAll();
Grade queryOne(int id);
int insert(Grade grade);
// 批量插入学生
int insertBatchStudent(List<Student> students);
int updateBatchStudent(List<Student> students);
int deleteBatchStudent(List<Integer> ids);
}
  • 实现grade接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package xyz.shi.dao.impl;

import org.apache.ibatis.session.SqlSession;
import xyz.shi.dao.GradeDao;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;
import xyz.shi.utils.MybatisUtils;

import java.util.List;

public class GradeDaoImpl implements GradeDao {
@Override
public List<Grade> findAll() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 命名空间+id,这就是命名空间的作用:便于区分
List<Grade> gradesList = sqlSession.selectList("gradeMapper.findAll");
sqlSession.commit();
sqlSession.close();
return gradesList;
}

@Override
public Grade queryOne(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
Grade grade = sqlSession.selectOne("gradeMapper.queryOne", id);
sqlSession.close();
return grade;
}

@Override
public int insert(Grade grade) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("gradeMapper.insertGrade", grade);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int insertBatchStudent(List<Student> students) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("gradeMapper.insertBatchStudent",students);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int updateBatchStudent(List<Student> students) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("gradeMapper.updateBatchStudent",students);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int deleteBatchStudent(List<Integer> ids) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.delete("gradeMapper.deleteBatchStudent",ids);
sqlSession.commit();
sqlSession.close();
return num;
}
}

  • 执行测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.junit.Test;
import xyz.shi.dao.GradeDao;
import xyz.shi.dao.PersonDao;
import xyz.shi.dao.StudentDao;
import xyz.shi.dao.impl.GradeDaoImpl;
import xyz.shi.dao.impl.PersonDaoImpl;
import xyz.shi.dao.impl.StudentDaoImpl;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Person;
import xyz.shi.domain.Student;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

public class GradeDaoTest {
private GradeDao gradeDao = new GradeDaoImpl();
@Test
public void findAll() {
//在查询之前,设置分页条件 显示第一页,展示10条数据
Page<Object> page = PageHelper.startPage(1, 10);

List<Grade> gradeList = gradeDao.findAll();

PageInfo<Grade> pageInfo = new PageInfo<>(gradeList, 5);

for (Grade grade:gradeList) {
System.out.println("------每个用户的信息-------");
System.out.println(grade.getName());
System.out.println(grade.getStudents());
List<Student> students = grade.getStudents();
for(Student student: students) {
System.out.println(student.getName());

}

}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}
@Test
public void queryOne() {
Grade grade = gradeDao.queryOne(2);
System.out.println(grade.getName());
List<Student> students = grade.getStudents();
for(Student student: students) {
System.out.println(student.getName());
}
}
@Test
public void queryOneStudent(){
StudentDao studentDao = new StudentDaoImpl();
Student student = studentDao.queryOne(3);
System.out.println(student);
}
@Test
public void insertGrade() {
Grade grade = new Grade();
grade.setName("初中");
gradeDao.insert(grade);
}
@Test
public void insertBatchStudent() {
Grade grade = new Grade();
grade.setName("初二");
gradeDao.insert(grade);

List<Student> students = new ArrayList<>();

Student student1 = new Student();
student1.setName("s1");
student1.setGrade(grade);
students.add(student1);

Student student2 = new Student();
student2.setName("s2");
student2.setGrade(grade);
students.add(student2);

gradeDao.insertBatchStudent(students);
}
@Test
public void updateBatchStudent() {
Grade grade = new Grade();
grade.setId(25);

List<Student> students = new ArrayList<>();
students.add(new Student(41, "新姓名1", grade));
students.add(new Student(42, "新姓名2", grade));
students.add(new Student(43, "新姓名3", grade));

gradeDao.updateBatchStudent(students);
}

@Test
public void deleteBatchStudent() {
List<Integer> ids = new ArrayList<>();
ids.add(3);
ids.add(4);
ids.add(50);
gradeDao.deleteBatchStudent(ids);
// 如果想删除班级和学生呢,可以先删除学生然后再次删除年纪,就是写两个delete语句
//delete from student where gid = #{gid}
//delete from grade where id = #{gid}
}

}

多对多

  • 直接用表student、course(课程表)

  • 直接用Student、course实体类

  • src-main-resources-studentMapper.xml 新建如下student表的增删改查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?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="studentMapper">
<!-- 定义封装resultMap
property 实体中对应的属性
column 数据库对应的列(select查询返回的列表)
-->
<resultMap type="xyz.shi.domain.Student" id="StudentGradeResult">
<id column="sid" jdbcType="BIGINT" property="id" />
<result column="sname" jdbcType="VARCHAR" property="name" />
<!-- 多对一关系的时候,多方使用association标签映射一方-->
<!-- <association property="grade" resultMap="StudentResult"></association>-->
<association property="grade" javaType="xyz.shi.domain.Grade">
<id column="gid" property="id" />
<result column="gname" property="name" />
</association>
</resultMap>

<resultMap type="xyz.shi.domain.Student" id="StudentCourseResult">
<id column="sid" jdbcType="BIGINT" property="id" />
<result column="sname" jdbcType="VARCHAR" property="name" />
<!-- 一对多 -->
<collection property="courses" ofType="xyz.shi.domain.Course">
<id property="id" column="cid"/>
<result property="name" column="cname"/>

</collection>
</resultMap>
<select id="queryOne" parameterType="int" resultMap="StudentCourseResult">
select s.name as sname,g.name as gname, g.id as gid, s.id as sid from student s,grade g where g.id = s.gid and s.id=#{id}
</select>
<select id="findAllStuCourse" resultMap="StudentCourseResult">
SELECT c.name as cname, c.id as cid, s.id as sid, s.name as sname
FROM student s
INNER JOIN student_course sc ON s.id = sc.student_id
INNER JOIN course c ON sc.course_id = c.id
</select>

<insert id="insertStudent" parameterType="xyz.shi.domain.Student"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO student (name, gid) VALUES (#{name}, #{grade.id})
</insert>
<!---学生关联多个课程 -->
<insert id="batchInsertStudentCourse">
INSERT INTO student_course (student_id, course_id)
VALUES
<foreach collection="courseIds" item="courseId" separator=",">
(#{studentId}, #{courseId})
</foreach>
</insert>
<update id="updateStudent" parameterType="Student">
UPDATE student
SET name = #{name}
WHERE id = #{id}
</update>
<update id="updateStudentCourse" parameterType="java.util.Map">
UPDATE student SET name = #{student.name} WHERE id = #{student.id};
<!-- 先清空关联关系,然后重写插入 -->
DELETE FROM student_course WHERE student_id = #{student.id};
<foreach collection="courseIds" item="courseId" separator=";">
INSERT INTO student_course (student_id, course_id) VALUES (#{student.id}, #{courseId})
</foreach>
</update>
<delete id="deleteStudentAndRelations" parameterType="int">
DELETE FROM student WHERE id = #{id};
DELETE FROM student_course WHERE student_id = #{id};
</delete>
</mapper>
  • studentDao接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package xyz.shi.dao;

import xyz.shi.domain.Course;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;

import java.util.List;
import java.util.Map;

public interface StudentDao {
Student queryOne(int id);
List<Student> findAll();
int insert(Student student);
int batchInsertStudentCourse(Map<String, Object> parameterMap);
int updateStudent(Student student);
int updateStudentCourse(Map<String, Object> parameterMap);
int deleteStudentAndRelations(Integer id);
}
  • 实现studentDao接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package xyz.shi.dao.impl;

import org.apache.ibatis.session.SqlSession;
import xyz.shi.dao.StudentDao;
import xyz.shi.domain.Student;
import xyz.shi.utils.MybatisUtils;

import java.util.List;
import java.util.Map;

public class StudentDaoImpl implements StudentDao {

@Override
public Student queryOne(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
Student student = sqlSession.selectOne("studentMapper.queryOne", id);
sqlSession.close();
return student;
}

@Override
public List<Student> findAll() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
List<Student> students = sqlSession.selectList("studentMapper.findAllStuCourse");
sqlSession.commit();
sqlSession.close();
return students;

}

@Override
public int insert(Student student) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("studentMapper.insertStudent", student);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int batchInsertStudentCourse(Map<String, Object> parameterMap) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("studentMapper.batchInsertStudentCourse", parameterMap);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int updateStudent(Student student) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("studentMapper.updateStudent", student);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int updateStudentCourse(Map<String, Object> parameterMap) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("studentMapper.updateStudentCourse", parameterMap);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int deleteStudentAndRelations(Integer id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("studentMapper.deleteStudentAndRelations", id);
sqlSession.commit();
sqlSession.close();
return num;
}
}

  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.junit.Test;
import xyz.shi.dao.GradeDao;
import xyz.shi.dao.PersonDao;
import xyz.shi.dao.StudentDao;
import xyz.shi.dao.impl.StudentDaoImpl;
import xyz.shi.domain.Course;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Person;
import xyz.shi.domain.Student;

import java.util.*;

public class StudentDaoTest {
private StudentDao studentDao = new StudentDaoImpl();

@Test
public void findAllStuCourse() {
//在查询之前,设置分页条件 显示第一页,展示10条数据
Page<Object> page = PageHelper.startPage(1, 3);

List<Student> students = studentDao.findAll();

PageInfo<Student> pageInfo = new PageInfo<>(students, 5);

for (Student student:students) {
System.out.println("------每个用户的信息-------");
System.out.println(student);
System.out.println(student.getCourses());
List<Course> courses = student.getCourses();
for(Course course: courses) {
System.out.println(course.getName());
System.out.println(course.getId());
}
}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}

@Test
public void insertBatchStudent() {
// 设置年纪
Grade grade = new Grade();
grade.setId(2);

Student student1 = new Student();
student1.setName("刘小龙1");
// 关联当前学生的年级
student1.setGrade(grade);
studentDao.insert(student1);

Map<String, Object> parameterMap = new HashMap<>();
parameterMap.put("studentId", student1.getId());
// 手动设置多个科目信息,也可以做批量新增科目
List<Integer> courseIds = Arrays.asList(17, 18);
parameterMap.put("courseIds", courseIds);

studentDao.batchInsertStudentCourse(parameterMap);
}
@Test
public void updateStudentCourse() {
// 要更新的学生信息
Student student = new Student();
student.setId(41);
// 学生新的名字
student.setName("张三");

Map<String, Object> parameterMap = new HashMap<>();
parameterMap.put("student", student);
// 手动设置多个科目信息,也可以做批量新增科目
List<Integer> courseIds = Arrays.asList(29, 30);
parameterMap.put("courseIds", courseIds);
// 更新学生的关联的课程
studentDao.updateStudentCourse(parameterMap);
}
@Test
public void deleteStudentAndRelations() {
studentDao.deleteStudentAndRelations(44);
}
}

总结

  • mapper中resultMap 下用collection为一对多的关联,用association为一对一的关联

  • 一对多或者多对多,更新关系时,先批量删除,然后再批量插入

  • 批量修改数据,需要在db字符串中加上&allowMultiQueries=true

  • 缓存,本次不做演练看目标博客即可

  • 代码在这里

说明

  • 本次主要针对orm中mybatis和Hibernate的关系映射进行实践

  • 本地环境:mysql 8.0,java 1.8,idea社区版本

  • 首先需要阅读这篇文章 介绍了对Hibernate的使用

  • 本次实践关系映射

一对一

  • 一对一的外键字段可以建在任何一张表中,但是,推荐建在查询频率较高的一张表中
  • 准备如下两个表
1
2
3
4
5
6
7
8
9
10
11
12
13

create table t_idCard(
id int primary key auto_increment comment '信用卡id',
cardNo varchar(255) comment '信用卡编号'
);

create table t_person (
id int primary key auto_increment comment '人员的id',
name varchar(32) comment '人员信息',
t_idCard_id int comment '信用卡id',
foreign key(t_idCard_id) references t_idCard(id)
);

foreign key(t_idCard_id) references t_idCard(id) 表示就是t_idCard_id是t_person的外键

  • 新增Person实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package xyz.shi.domain;

public class Person {

private int id;

private String name;

private IdCard idCard;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public IdCard getIdCard() {
return idCard;
}

public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
}
  • 新增idcard实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package xyz.shi.domain;

public class IdCard {
private int id;
private String cardNo;
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getCardNo() {
return cardNo;
}

public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}
}

  • 分页的实例类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package xyz.shi.entity;
import java.util.List;

public class QueryResult {
private int count; // 总记录数
private List list; // 一页的数据
public void setCount(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void setList(List list) {
this.list = list;
}
public List getList() {
return list;
}
public QueryResult(int count, List list) {
this.count = count;
this.list = list;
}
@Override
public String toString() {
return "QueryResult{" +
"count=" + count +
", list=" + list +
'}';
}
}


  • 实体 src-man-resources-IdCard.hbm.xml对应的xml配置文件
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="xyz.shi.domain.IdCard" table="t_idCard">
<id name="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
</class>
</hibernate-mapping>
  • 实体 src-man-resources-Persion.hbm.xml对应的xml配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="xyz.shi.domain">
<class name="Person" table="t_person">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<!-- 一对一配置,设置属性unique=”true”就变成了一对一 -->
<!-- 配置一对一的关系,基于外键的方式 -->
<many-to-one name="idCard" column="t_idCard_id" unique="true" cascade="all" ></many-to-one>
</class>
</hibernate-mapping>

外键有两种关联关系:

1.基于主键的方式,我这里一直失败,Persion外键idCard的值一直为空

1
2
3
4
5
6
7
<!-- 共享主键用此 (一对一关系) -->
<id name="id">
<generator class="foreign">
<param name="property">idCard</param>
</generator>
</id>
<one-to-one name="idCard" class="IdCard" constrained="true"></one-to-one> -

2.基于外键的方式,这里的cascade是级联关联,一定要加上,不然报错

cascade属性:

all :: 所有情况下均进行关联操作。
none: 所有情况下均不进行关联操作。这是默认值。
save-update: 在执行save/update/saveOrUpdate时进行关联操作。
delete: 在执行delete时进行关联操

-

  • 在 Hibernate 核心配置文件 src-man-resources-hibernate.cfg.xml 中,使用 元素来指定映射文件 Student.hbm.xml 和 Grade.hbm.xml 的位置信息,配置代码如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置关于数据库连接的四个项:driverClass url username password -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>

<!-- 可以将向数据库发送的SQL语句显示出来 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化SQL语句 -->
<property name="hibernate.format_sql">true</property>
<!-- hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 配置hibernate的映射文件所在的位置 -->
<mapping resource="IdCard.hbm.xml"/>
<mapping resource="Person.hbm.xml"/>
</session-factory>
</hibernate-configuration>

  • Dao代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package xyz.shi.dao;

import org.hibernate.Session;
import org.hibernate.Transaction;
import xyz.shi.domain.Person;
import xyz.shi.entity.QueryResult;
import xyz.shi.utils.HibernateUtils;

import java.util.List;

public class PersonDao {
/*
* 保存
*/
public void save(Person person) {
Session session = HibernateUtils.openSession();
try {
Transaction tx = session.beginTransaction(); // 开启事务
session.save(person);
tx.commit(); // 提交事务
} catch (RuntimeException e) {
session.getTransaction().rollback(); // 回滚事务
throw e;
} finally {
session.close(); // 关闭session
}
}

/*
* 更新
*/
public void update(Person person) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

session.update(person);// 操作

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 删除
*/
public void delete(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

Object user = session.get(Person.class, id); // 要先获取到这个对象
session.delete(user); // 删除的是实体对象

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 根据id查询一个User数据
*/
public Person getById(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Person person = (Person) session.get(Person.class, id);// 操作
tx.commit();
return person;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 查询所有
*/
public List<Person> findAll() {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

// 方式一:使用HQL语句
// 这里的FROM User 并不是表的名字,而是User.hbm.xml中的<class name="User" 这个name
List<Person> list = session.createQuery("FROM Person").list(); // 使用HQL查询

tx.commit();
return list;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/**
* 分页的查询数据列表
* @param firstResult 从结果列表中的哪个索引开始取数据
* @param maxResults 最多取多少条数据
* @return 一页的数据列表
*/
@SuppressWarnings("unchecked")
public QueryResult findAll(int firstResult, int maxResults) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// 查询一页的数据列表
// 方式一:
// Query query = session.createQuery("FROM User");
// query.setFirstResult(firstResult);
// query.setMaxResults(maxResults);
// List<User> list = query.list(); // 使用HQL查询

// 方式二:方法链
List<Person> list = session.createQuery( //
"FROM Person") //
.setFirstResult(firstResult) //
.setMaxResults(maxResults) //
.list();

// 查询总记录数
// session.createQuery("SELECT COUNT(*) FROM User").list().get(0);
// Long count = (Long) session.createQuery("SELECT COUNT(*) FROM User").uniqueResult();
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM Person") //
.uniqueResult();
tx.commit();
// 返回结果
return new QueryResult(count.intValue(), list);
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
}
  • 测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.Person;
import xyz.shi.domain.IdCard;
import xyz.shi.entity.QueryResult;

import java.util.List;

public class PersonDaoTest {
private PersonDao personDao = new PersonDao();

@Test
public void saveTest() {
// 先创建idcard
IdCard idCard = new IdCard();
idCard.setCardNo("1111111");

// 创建persion
Person person = new Person();
person.setName("王大伟");

person.setIdCard(idCard);
personDao.save(person);
}
@Test
public void findIdTest() {
personDao.getById(16);
}

// 修改操作
@Test
public void updateTest() {

Person person = new Person();
person.setId(16);
person.setName("郑1");
personDao.update(person);

}

// 删除操作---根据id进行删除
@Test
public void deleteTest() {
personDao.delete(19);
}

// 查询所有User
@Test
public void findAllTest() {
List<Person> list = personDao.findAll();
for (Person user : list) {
System.out.println(user);
}

}
@Test
public void findAll1() {
QueryResult result = personDao.findAll(0, 5);
System.out.println(result.getCount());
for (Object o : result.getList()) {
System.out.println(o);
}
}
}

一对多

  • 在三种关联关系中,一对多(或者多对一)是最常见的一种关联关系。

  • 在关系型数据库中,一对多映射关系通常是由“多”的一方指向“一”的一方。在表示“多”的一方的数据表中增加一个外键,指向“一”的一方的数据表的主键,“一”的一方称为主表,而“多”的一方称为从表

  • 准备如下两个表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
student 表为学生表,id 为学生表的主键,name 表示学生名称,gid 为学生表的外键,指向班级表的主键 id;
grade 表为班级表,id 为班级表的主键,name 表示班级名称;

create table grade(
id int primary key auto_increment comment '主键id',
name varchar(128) comment '班级'
);

create table student(
id int primary key auto_increment comment '主键id',
name varchar(128) comment '学生名字',
gid int comment '班级id',
foreign key(gid) references grade(id)
)comment 'gid是student表的外键';


  • 新增Student实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package xyz.shi.domain;


public class Student {
private Integer id;
private String name;
//持有实体类 Grade 的一个引用,维护多对一关系
private Grade grade;

public Student() {
}

public Student(Integer id, String name) {
this.id = id;
this.name = name;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Grade getGrade() {
return grade;
}

public void setGrade(Grade grade) {
this.grade = grade;
}

@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", grade=" + grade +
'}';
}
}
  • 新增Grade实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package xyz.shi.domain;


import java.util.HashSet;
import java.util.Set;

public class Grade {
private Integer Id;
private String name;
//持有 Student 引用的集合,来维护一对多关联关系
private Set<Student> students = new HashSet<>();

public Integer getId() {
return Id;
}

public void setId(Integer id) {
Id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Set<Student> getStudents() {
return students;
}

public void setStudents(Set<Student> students) {
this.students = students;
}

@Override
public String toString() {
return "Grade{" +
"Id=" + Id +
", name='" + name +
'}';
}
}
  • 创建 Student 的映射文件 src-man-resources-Student.hbm.xml,配置如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="xyz.shi.domain.Student" table="student" schema="shi_jdbc" lazy="true">
<!--主键映射-->
<id name="id" column="id" type="java.lang.Integer">
<!--主键生成策略-->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="100" type="java.lang.String"></property>

<!--维护关联关系-->
<many-to-one name="grade" class="xyz.shi.domain.Grade" column="gid" cascade="all"/>
</class>
</hibernate-mapping>

注意many-to-one用法,cascade=”all”为级联关系

  • 创建 Grade 的映射文件 src-man-resources-Grade.hbm.xml,配置如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="xyz.shi.domain.Grade" table="grade" schema="shi_jdbc">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<property name="name" column="name" length="100" type="java.lang.String"/>
<!--使用 set 元素维护一对多关联关系-->
<set name="students">
<key column="gid"></key>
<one-to-many class="xyz.shi.domain.Student"></one-to-many>
</set>
</class>
</hibernate-mapping>

从 Grade(班级)的角度看,Student 和 Grade 是一对多的关系,因此 Grade 的映射文件中,需要通过 标签来维护 Grade 与 Student 的一对多关联关系。

  • 在 Hibernate 核心配置文件 src-man-resources-hibernate.cfg.xml 中,使用 元素来指定映射文件 Student.hbm.xml 和 Grade.hbm.xml 的位置信息,配置代码如下。

    1
    2
    3
    4
    ...
    <!-- 配置hibernate的映射文件所在的位置 -->
    <mapping resource="Student.hbm.xml"/>
    <mapping resource="Grade.hbm.xml"/>
  • Dao代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package xyz.shi.dao;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Person;
import xyz.shi.domain.Student;
import xyz.shi.entity.QueryResult;
import xyz.shi.utils.HibernateUtils;

import java.util.List;

public class StudentDao {
public void save(Student student) {
Session session = HibernateUtils.openSession();
try {
Transaction tx = session.beginTransaction(); // 开启事务
session.save(student);
tx.commit(); // 提交事务
} catch (RuntimeException e) {
session.getTransaction().rollback(); // 回滚事务
throw e;
} finally {
session.close(); // 关闭session
}
}

/*
* 更新
*/
public void update(Student student) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Student student1 = session.get(Student.class, student.getId());
// 设置学生信息
student1.setName(student.getName());
// 设置班级
Grade grade = student1.getGrade();
student.setGrade(grade);
//我们修改了该对象的属性,并且不需要显式调用 session.saveOrUpdate(student),Hibernate 会自动将修改后的对象同步到数据库。
// session.update(student);// 操作
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 删除
*/
public void delete(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

Object user = session.get(Student.class, id); // 要先获取到这个对象
session.delete(user); // 删除的是实体对象

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 根据id查询,返回对象为List object,然后直接转换到各自的实体类
*/
public List<Object[]> getById(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
List<Object[]> list = session.createQuery(
"select s, g from Student s join s.grade g where s.id="+id).list();
tx.commit();
return list;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 查询所有
*/
public List<Object[]> findAll() {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// 注意这里的用法用的原生语句,要给别名不然报错
List<Object[]> list = session.createSQLQuery("select s.id as student_id, s.name as student_name, g.id as grade_id, g.name as grade_name from student s join grade g on s.gid=g.id").list();
tx.commit();
return list;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/**
* 分页的查询数据列表
* @param firstResult 从结果列表中的哪个索引开始取数据
* @param maxResults 最多取多少条数据
* @return 一页的数据列表
*/
@SuppressWarnings("unchecked")
public QueryResult findAll(int firstResult, int maxResults) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

List<Object[]> list = session.createQuery( //
"select s, g from Student s join s.grade g") //
.setFirstResult(firstResult) //
.setMaxResults(maxResults) //
.list();

// 查询总记录数
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM Student") //
.uniqueResult();
tx.commit();


// 返回结果
return new QueryResult(count.intValue(), list);
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
}

  • session.createQuery( “select s, g from Student s join s.grade g”) 注意这里的用法,不支持join,s.grade其实就是Student实体类类设置的
  • createSQLQuery 支持用原生语句查询
  • 一对多查询,返回List<Object[]>,然后各自的实体类去转换
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;
import xyz.shi.dao.StudentDao;
import xyz.shi.domain.Student;
import xyz.shi.domain.Grade;
import xyz.shi.entity.QueryResult;

import java.util.List;

public class StudentDaoTest {
private StudentDao studentDao = new StudentDao();

@Test
public void saveTest() {
Grade grade = new Grade();
grade.setName("三年级");

Student student = new Student();
student.setName("王大伟");
//设置学生的班级
student.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红");

studentDao.save(student);
studentDao.save(student2);
}
@Test
public void findIdTest() {
List<Object[]> students = studentDao.getById(12);
for (Object[] result : students) {
Student student = (Student) result[0];
Grade grade = (Grade) result[1];
System.out.println(student);
System.out.println(grade);
}
}

// 修改操作
@Test
public void updateTest() {
Student student = new Student();
student.setId(3);
student.setName("郑122");
studentDao.update(student);


}

// 删除操作---根据id进行删除
@Test
public void deleteTest() {
studentDao.delete(2);
}

// 查询所有
@Test
public void findAllTest() {

List<Object[]> list = studentDao.findAll();
for (Object[] result : list) {
int studentId = (int) result[0];
String studentName = (String) result[1];
int gradeId = (int) result[2];
String gradeName = (String) result[3];

Student student = new Student();
student.setId(studentId);
student.setName(studentName);

Grade grade = new Grade();
grade.setId(gradeId);
grade.setName(gradeName);

student.setGrade(grade);
System.out.println(student);
System.out.println(grade);

}

}
@Test
public void findAll1() {
QueryResult result = studentDao.findAll(0, 5);
System.out.println(result.getCount());
List<Object[]> list = result.getList();
for (Object[] arr : list) {
Student student = (Student) arr[0];
System.out.println("Student: " + student.getName() + ", Grade: " + student.getGrade().getName());

}

}
}


多对多

  • 在实际的应用中,“多对多”也是一种常见的关联关系,例如学生和课程的关系,一个学生可以选修多门课程,一个课程可以被多名学生选修。
  • 在关系型数据库中,是无法直接表达“多对多”关联关系的,我们一般会采用新建一张中间表,将一个“多对多”关联拆分为两个“一对多”关联解决此问题,如下图

image-20231226111134866

  • 创建如下数据表关系,图书和作者,他们是多对多的关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-- 创建学生表,用上面的一对多的表即可
create table student(
id int primary key auto_increment comment '主键id',
name varchar(128) comment '学生名字',
gid int comment '班级id',
foreign key(gid) references grade(id)
)
-- 创建课程表
create table course(
id int primary key auto_increment comment '主键id',
name varchar(32) comment '课程名'
);

-- 建立第三张表来保存两张表的关系
create table student_course(
id int primary key auto_increment comment '联表的id',
student_id int comment '学生的id',
course_id int comment '课程的id',
foreign key(student_id) references student(id)
on update cascade
on delete cascade,
foreign key(course_id) references course(id)
on update cascade
on delete cascade
);

  • 创建课程实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package xyz.shi.domain;

import java.util.HashSet;
import java.util.Set;

/**
* 课程实体类
*/
public class Course {
private Integer id;
private String name;
//学生 Student 的集合作为其属性,维护多对多关联关系
private Set<Student> students = new HashSet<>();
public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Set<Student> getStudents() {
return students;
}

public void setStudents(Set<Student> students) {
this.students = students;
}

@Override
public String toString() {
return "Course{" +
"id=" + id +
", name='" + name +
'}';
}
}
  • 修改 Student 实体类的代码,在 Student 类中以 Set 的形式引入 Course 对象作为其属性,来维护 Student 与 Course 之间的多对多关联关系,代码如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
package xyz.shi.domain;
....
public class Student {
//将 Course 对象的集合作为其属性,以维护它们之间的多对多关联关系
private Set<Course> courses = new HashSet<>();
public Set<Course> getCourses() {
return courses;
}

public void setCourses(Set<Course> courses) {
this.courses = courses;
}

  • 创建 Course 的映射文件 src-man-resources-Coures.hbm.xml,具体配置如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="xyz.shi.domain">
<class name="Course" table="course" schema="jdbc">
<id name="id" column="id" >
<generator class="native"></generator>
</id>

<property name="name" column="name" length="100"/>

<set name="students" table="student_course" cascade="save-update" inverse="true">
<key column="course_id"></key>
<many-to-many class="Student" column="student_id"></many-to-many>
</set>
</class>
</hibernate-mapping>

inverse(反转)属性,它的作用是控制关联的双方由哪一方管理关联关系。

inverse=”true” 如果两个映射文件的inverse都设为false(默认),则会出现异常(主键重复)导致插入失败

两映射文件中的inverse都为true,则Student和Course都去维护关联关系,即同时向连接表中插入记录,则会导致主键重复而插入失败。

将其中一方的inverse设为true,让对方维持关联关系;

  • 修改student.hbm.xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="xyz.shi.domain.Student" table="student" schema="shi_jdbc" lazy="true">
<!--主键映射-->
<id name="id" column="id" type="java.lang.Integer">
<!--主键生成策略-->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="100" type="java.lang.String"></property>
<!--维护关联关系-->
<many-to-one name="grade" class="xyz.shi.domain.Grade" column="gid"/>
<set name="courses" table="student_course" lazy="false">
<key column="student_id"></key>
<many-to-many class="xyz.shi.domain.Course" column="course_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;
import xyz.shi.dao.StudentDao;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;
import xyz.shi.domain.Course;
import xyz.shi.entity.QueryResult;
import xyz.shi.utils.HibernateUtils;

import java.util.List;
import java.util.Set;

public class StudentDao2Test {

@Test
public void saveTest() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();

//新建学生和班级信息
Grade grade = new Grade();
grade.setName("三年级");
Student student = new Student();
student.setName("选课学生1");
student.setGrade(grade);
Student student2 = new Student();
student2.setName("选课学生2");
student2.setGrade(grade);
grade.getStudents().add(student);
grade.getStudents().add(student2);

//新建三个课程
Course course = new Course();
course.setName("Java");
Course course2 = new Course();
course2.setName("PHP");
Course course3 = new Course();
course3.setName("C++");

//保存操作
session.save(student);
session.save(student2);
session.save(grade);
session.save(course);
session.save(course2);
session.save(course3);

//学生选课的关系相互关联
course.getStudents().add(student);
student.getCourses().add(course);

course2.getStudents().add(student2);
student2.getCourses().add(course2);

course3.getStudents().add(student2);
student2.getCourses().add(course3);

//提交事务
transaction.commit();
//释放资源
session.close();

}
@Test
public void findIdTest() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
String hql = "SELECT s, c, g FROM Student s " +
"JOIN s.courses c " +
"JOIN s.grade g where s.name = :name";
Query query = session.createQuery(hql);
query.setParameter("name", "选课学生2");

List<Object[]> students = query.list();
for (Object[] result : students) {
Student student = (Student) result[0];
Course course = (Course) result[1];
Grade grade = (Grade) result[2];
System.out.println(student);
System.out.println(course);
System.out.println(grade);
}

//提交事务
transaction.commit();
//释放资源
session.close();

}

// 修改操作
@Test
public void updateTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
Transaction tx = session.getTransaction();
if (!tx.isActive()) {
tx.begin();
}

Student student = session.get(Student.class, 42);
student.setName("我是谁1");
// 设置学生的班级
Grade grade = session.get(Grade.class, 2);
student.setGrade(grade);

Course course = session.get(Course.class, 33);

// 先移除旧关联关系
for (Course existingCourse : student.getCourses()) {
existingCourse.getStudents().remove(student);
}

// 添加新关联关系
student.getCourses().clear();
student.getCourses().add(course);
course.getStudents().add(student);


// 保存实体对象到数据库中
session.saveOrUpdate(student);
session.saveOrUpdate(course);

if (tx.isActive()) {
tx.commit();
session.close();
sessionFactory.close();
}
}

// 删除操作
@Test
public void deleteTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
Transaction tx = session.getTransaction();
if (!tx.isActive()) {
tx.begin();
}

Student student = session.get(Student.class, 40);
Course course = session.get(Course.class, 31);

// 移除关联关系
student.getCourses().remove(course);
course.getStudents().remove(student);
// 删除学生信息
session.delete(student);

if (tx.isActive()) {
tx.commit();
session.close();
sessionFactory.close();
}
}

// 查询所有
@Test
public void findAllTest() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
// 根据业务来编写代码
String hql = "SELECT s, c, g FROM Student s " +
"JOIN s.courses c " +
"JOIN s.grade g";
Query query = session.createQuery(hql);
List<Object[]> students = query.list();
for (Object[] result : students) {
Student student = (Student) result[0];
Course course = (Course) result[1];
Grade grade = (Grade) result[2];
System.out.println(student);
System.out.println(course);
System.out.println(grade);
}
//提交事务
transaction.commit();
//释放资源
session.close();

}
@Test
public void findAll1() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();

String hql = "SELECT s, c, g FROM Student s " +
"JOIN s.courses c " +
"JOIN s.grade g";
Query query = session.createQuery(hql);
query.setFirstResult(0);
query.setMaxResults(3);
List<Object[]> students = query.list();
for (Object[] result : students) {
Student student = (Student) result[0];
Course course = (Course) result[1];
Grade grade = (Grade) result[2];
System.out.println(student);
System.out.println(course);
System.out.println(grade);
}
//提交事务
transaction.commit();
//释放资源
session.close();

}
}

总结

简单搭建

  • 环境:java 1.8 ,idea 社区版本,mysql 8.0
  • pom.xml依赖文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.2.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
  • 核心配置文件src-main-java-resources-hibernate.cfg.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置关于数据库连接的四个项:driverClass url username password -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>

<!-- 可以将向数据库发送的SQL语句显示出来 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化SQL语句 -->
<property name="hibernate.format_sql">true</property>
<!-- hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 配置hibernate的映射文件所在的位置 -->
<mapping resource="User.hbm.xml"/>
</session-factory>
</hibernate-configuration>

  • 实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package xyz.shi.domain;

public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public User () {

}
public User(int id,String name, String password) {
this.name = name;
this.id = id;
this.password = password;
}

public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", password=" + password +
'}';
}
}
  • src-main-java-resources-User.hbm.xml 映射文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="xyz.shi.domain">
<!--
name:即实体类的全名
table:映射到数据库里面的那个表的名称
catalog:数据库的名称
-->
<class name="User" table="users" catalog="test">
<!-- class下必须要有一个id的子元素 -->
<!-- id是用于描述主键的 -->
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"/>
</id>
<!--
使用property来描述属性与字段的对应关系
如果length忽略不写,且你的表是自动创建这种方案,那么length的默认长度是255
-->
<property name="name" column="name" length="20"/>
<property name="password" column="password" length="20"/>
</class>
</hibernate-mapping>

  • 测试:src-test-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;
import xyz.shi.domain.User;
import java.util.List;

public class HibernateTest {

// 保存一个User
@Test
public void saveUserTest() {
// 创建一个User
User c = new User();
c.setName("叶子");
c.setPassword("1111");
// 使用Hibernate的API来完成将User信息保存到mysql数据库中的操作
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 操作
session.save(c);
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

// 根据id查询一个User对象
@Test
public void findUserByIdTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 根据业务来编写代码
User c = session.get(User.class, 10);
System.out.println(c.getName());
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

// 修改操作
@Test
public void updateUserTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 根据业务来编写代码
User c = session.get(User.class, "62");
c.setName("郑敏");
session.update(c); // 修改操作
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

// 删除操作---根据id进行删除
@Test
public void deleteUserTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 根据业务来编写代码
User c = session.get(User.class, "62");
session.delete(c); // 删除操作
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

// 查询所有User
@Test
public void findAllUserTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 根据业务来编写代码
Query query = session.createQuery("from User"); // HQL语句,它类似于SQL语句
List<User> list = query.list();
for (User user : list) {
System.out.println(user);
}
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

}

优化代码

  • 提取公共类HibernateUtils
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package xyz.shi.domain.utils;


import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {

// SessionFactory全局只需要有一个就可以了,因为它的创建和销毁需要消耗大量的资源,初始化信息会比较多,并且它是线程安全的,可以在多线程的环境下使用它
private static SessionFactory sessionFactory;

static {
// 初始化SessionFactory方式一:
/*
Configuration cfg = new Configuration(); // 代表配置文件的一个对象
cfg.configure(); // 读取默认的配置文件(hibernate.cfg.xml)
// cfg.configure("hibernate.cfg.xml"); // 读取指定位置的配置文件
sessionFactory = cfg.buildSessionFactory();
*/

// 初始化SessionFactory方式二:
sessionFactory = new Configuration() //
.configure() //
.buildSessionFactory(); // 方法链
}

/**
* 获取全局唯一的SessionFactory
*
* @return
*/
public static SessionFactory getSessionFactory() {
return sessionFactory;
}

/**
* 从全局唯一的SessionFactory中打开一个Session
*
* @return
*/
public static Session openSession() {
return sessionFactory.openSession();
}

}

-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package xyz.shi.entity;
import java.util.List;

public class QueryResult {

private int count; // 总记录数
private List list; // 一页的数据

public void setCount(int count) {
this.count = count;
}

public int getCount() {
return count;
}
public void setList(List list) {
this.list = list;
}

public List getList() {
return list;
}

public QueryResult(int count, List list) {
this.count = count;
this.list = list;
}

@Override
public String toString() {
return "QueryResult{" +
"count=" + count +
", list=" + list +
'}';
}
}
  • dao层UserDao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package xyz.shi.dao;


import org.hibernate.Session;
import org.hibernate.Transaction;
import xyz.shi.domain.User;
import xyz.shi.entity.QueryResult;
import xyz.shi.utils.HibernateUtils;

import java.util.List;

public class UserDao {

/*
* 保存
*/
public void save(User user) {
Session session = HibernateUtils.openSession();
try {
Transaction tx = session.beginTransaction(); // 开启事务
session.save(user);
tx.commit(); // 提交事务
} catch (RuntimeException e) {
session.getTransaction().rollback(); // 回滚事务
throw e;
} finally {
session.close(); // 关闭session
}
}

/*
* 更新
*/
public void update(User user) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

session.update(user);// 操作

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 删除
*/
public void delete(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

Object user = session.get(User.class, id); // 要先获取到这个对象
session.delete(user); // 删除的是实体对象

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 根据id查询一个User数据
*/
public User getById(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
User user = (User) session.get(User.class, id);// 操作
tx.commit();
return user;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 查询所有
*/
public List<User> findAll() {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

// 方式一:使用HQL语句
// 这里的FROM User 并不是表的名字,而是User.hbm.xml中的<class name="User" 这个name
List<User> list = session.createQuery("FROM User").list(); // 使用HQL查询

tx.commit();
return list;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/**
* 分页的查询数据列表
* @param firstResult 从结果列表中的哪个索引开始取数据
* @param maxResults 最多取多少条数据
* @return 一页的数据列表
*/
@SuppressWarnings("unchecked")
public QueryResult findAll(int firstResult, int maxResults) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// 查询一页的数据列表
// 方式一:
// Query query = session.createQuery("FROM User");
// query.setFirstResult(firstResult);
// query.setMaxResults(maxResults);
// List<User> list = query.list(); // 使用HQL查询

// 方式二:方法链
List<User> list = session.createQuery( //
"FROM User") //
.setFirstResult(firstResult) //
.setMaxResults(maxResults) //
.list();

// 查询总记录数
// session.createQuery("SELECT COUNT(*) FROM User").list().get(0);
// Long count = (Long) session.createQuery("SELECT COUNT(*) FROM User").uniqueResult();
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM User") //
.uniqueResult();
tx.commit();

// 返回结果
return new QueryResult(count.intValue(), list);
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
}

session.createQuery(“FROM User”).list(),这里的FROM User 并不是表的名字,而是User.hbm.xml中的<class name=”User” 这个name

  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

import org.junit.Test;
import xyz.shi.dao.UserDao;
import xyz.shi.domain.User;
import xyz.shi.entity.QueryResult;

import java.util.List;

public class UserDaoTest {

private UserDao userDao = new UserDao();

@Test
public void save() {
User user = new User();
user.setName("2222");
user.setPassword("11");
userDao.save(user);
}

@Test
public void update() {
User byId = userDao.getById(10);
byId.setName("456");
userDao.update(byId);
}

@Test
public void delete() {
userDao.delete(3);
}

@Test
public void getById() {
System.out.println(userDao.getById(10));
}

@Test
public void findAll() {
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
}

@Test
public void findAll1() {
QueryResult result = userDao.findAll(0, 5);
System.out.println(result.getCount());
for (Object o : result.getList()) {
System.out.println(o);
}
}
}

  • 以上代码主要来自这里

说明

  • 本次教程主要来自这里
  • 环境信息:win10 、java1.8、IDEA 社区版本

整合JDBC

  • 用IDEA 社区版本新建个maven项目
  • pom.xml的依赖文件如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>spring-quickstart1</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<!--数据库连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
  • 实体类
1
2
3
4
5
6
7
8
9
package xyz.shi.entity;

import lombok.Data;
@Data
public class User {
private int id;
private String name;
private String password;
}
  • springconfig配置类,主要用于扫描路径
1
2
3
4
5
6
7
8
9
10
package xyz.shi.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("xyz.shi")
public class SpringConfig {
}

  • jdbc的配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package xyz.shi.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

//表明当前类是spring的一个配置类,作用是替代spring的applicationContext.xml。
// 但其本质就是@Component注解,被此注解修饰的类,也会被存入spring的ioc容器
@Configuration
public class JDBCConfig {
//@Bean通常出现在Spring的配置类当中,注解在方法上,表示把当前方法的返回值存入spring的ioc容器
@Bean
public DataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setPassword("123456");
hikariConfig.setUsername("root");
hikariConfig.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
hikariConfig.addDataSourceProperty("autoCommit", "true");
hikariConfig.addDataSourceProperty("connectionTimeout", "5");
hikariConfig.addDataSourceProperty("idleTimeout", "60");
return new HikariDataSource(hikariConfig);
}
@Bean
public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
  • service代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package xyz.shi.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Service;
import xyz.shi.entity.User;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

@Service
public class UserService1 {
@Autowired
private JdbcTemplate jdbcTemplate;

public int save(String name, String pwd) {
//该类返回新增记录时的自增长主键值。
KeyHolder keyHolder = new GeneratedKeyHolder();
int update = jdbcTemplate.update((connection) -> {
PreparedStatement ps = connection.prepareStatement("insert into users(name,password) values(?,?)", Statement.RETURN_GENERATED_KEYS);

ps.setObject(1, name);
ps.setObject(2, pwd);
return ps;
}, keyHolder);
if (update > 0){
System.out.println("保存成功...user id:"+keyHolder.getKey());
}
return keyHolder.getKey().intValue();
}

/**
* 根据用户id 查询
* @param id
* @return
*/
public User getUser(int id){
User user = jdbcTemplate.queryForObject("select * from users where id = ?", new Object[]{id},new BeanPropertyRowMapper<>(User.class));
return user;
}

/**
* 分页查询
* @param page
* @param pageSize
* @return
*/
public List<User> queryUserList(int page, int pageSize){
int index = (page-1)*pageSize;
int size = pageSize;
List<User> list = jdbcTemplate.query("select * from users limit ?,?", new Object[]{index, size}, new BeanPropertyRowMapper<>(User.class));
return list;
}

/**
* 更新
* @param id
* @param name
* @return
*/
public boolean update(int id,String name){
int update = jdbcTemplate.update("update users set name=? where id=?", name, id);
if (update > 0){
return true;
}
throw new RuntimeException("update error");
}

public boolean delete(int id){
int deleteCount = jdbcTemplate.update("delete from users where id=?", id);
return deleteCount > 0;
}
// execute的用法
public List<User> execute1(int page,int pageSize) {
return jdbcTemplate.execute((Connection conn) -> {
PreparedStatement ps = conn.prepareStatement("select * from users limit ?,?");
ps.setObject(1, (page - 1) * pageSize);
ps.setObject(2, pageSize);
ResultSet resultSet = ps.executeQuery();
List<User> userList = new ArrayList<>();
while (resultSet.next()) {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
user.setPassword(resultSet.getString("password"));
userList.add(user);
}
return userList;
});
}
}

注意execute的用法

  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import xyz.shi.config.SpringConfig;
import xyz.shi.entity.User;
import xyz.shi.service.UserService1;

import java.util.List;

public class jdbcTest {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

UserService1 userService = context.getBean(UserService1.class);
@Test
public void save() {
int id = userService.save("test1234","22222");
System.out.println(id);
}
@Test
public void find() {
User user = userService.getUser(46);
System.out.println(user);
}
@Test
public void queryUserList() {
List<User> users = userService.queryUserList(1, 2);
System.out.println(users);
}
@Test
public void update() {
userService.update(46, "哈哈");
}
@Test
public void delete() {
boolean flag = userService.delete(46);
System.out.println(flag);

}
@Test
public void execute1() {
List<User> users1 = userService.execute1(1,3);
System.out.println(users1);
}
}

整合Mybatis

SqlSessionFactory是mybatis的核心,当与spring进行整合时,我们使用mybatis-spring提供的SqlSessionFactoryBean 来创建其实例,SqlSessionFactoryBean实现了FactoryBean 接口

  • 依赖包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
	<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>spring-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<!--spring整合mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>

<!--mybatis环境-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--分页插件坐标-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
</dependencies>
<build>

<!--目的是把src/main/java目录中的xml文件包含到输出结果中,也就是输出到classes目录中-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>

</build>
</project>
  • 下面是xml的配置方式,resources/application.xml,主要配置了连接数据库、SqlSessionFactory等信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&amp;serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="autoCommit" value="true"/>
<property name="connectionTimeout" value="5000" />
<property name="idleTimeout" value="60" />
</bean>
<!--jdbc的xml配置-->
<bean id="myDataSource" class="com.zaxxer.hikari.HikariDataSource">
<constructor-arg name="configuration" ref="hikariConfig"/>
</bean>
<!--mybatis-->
<!--SqlSessionFactory-->
<!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库连接池付给dataSource属性-->
<property name="dataSource" ref="myDataSource"/>
<!--mybatis主配置文件的位置
configLocation属性是Resource类型,读取配置文件
它的赋值使用的是value , 指定文件的路径,使用的是classpath:表示文件的位置
-->
<property name="configLocation" value="classpath:mybatis.xml"/>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<prop key="helperDialect">mysql</prop>
</props>
</property>
</bean>
</array>
</property>
</bean>
<!--创建 dao对象
使用SqlSession的getMapper(userDao.class)
MapperScannerConfigurer在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定的是SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="SqlSessionFactory"/>
<!--指定包名,包名是dao接口所在的包名
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象
创建好的dao对象放入到spring的容器中

dao默认对象的名称:是接口名字的首字母小写
-->

<property name="basePackage" value="xyz.shi.dao"/>
<!--多个包-->
<!--<property name="basePackage" value="com.md.dao,com.md.dao2"/>-->

</bean>
<!--下面的就是自己定义的service-->
<!--声明service-->
<bean id="userService" class="xyz.shi.service.Impl.UserServiceImpl">
<!--就是上面通过创建的dao对象-->
<property name="userDao" ref="userDao"/>
</bean>
</beans>
  • resources/mybatis.xml,基本配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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:控制mybatis全局行为-->
<settings>

<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>


<!--设置别名-->
<typeAliases>
<!--
package:把包下面的所有类名作为别名
name:实体类所在的包名-->
<package name="xyz.shi.entity"/>

</typeAliases>



<!-- sql映射文件的位置 -->
<mappers>
<!-- 注册userMapper.xml文件 -->
<mapper resource="mapper/UserMapper.xml"/>
</mappers>

</configuration>

  • resources/mapper/userMapper.xml,写入sql信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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="xyz.shi.dao.UserDao">

<insert id="save" parameterType="xyz.shi.entity.User" keyProperty="id" useGeneratedKeys="true">
insert into users(name,password)
value(#{name},#{password})
</insert>
<update id="update" parameterType="xyz.shi.entity.User">
update users set name=#{name},password=#{password} where id=#{id}
</update>
<delete id="delete" parameterType="int">
delete from users where id=#{id}
</delete>
<select id="findById" parameterType="int" resultType="xyz.shi.entity.User">
select * from users where id = #{id};
</select>
<select id="findAll" resultType="xyz.shi.entity.User">
select * from users
</select>
</mapper>

记得一定要把resources目录设置为Resources Root,不然读取application.xml报错不存在此文件

  • 写mapper的接口,其实就是dao层,我用的dao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package xyz.shi.dao;

import com.github.pagehelper.Page;
import xyz.shi.entity.User;

// 注意这里的方法名称,一定要和mapper.xml中的id名称一致,不然无法映射到就会报错
public interface UserDao {
int save(User user);
int update(User user);
User findById(int id);
Page<User> findAll();
int delete(int id);
}

  • Service的Mybatis实现,增删改查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package xyz.shi.service;

import com.github.pagehelper.Page;
import org.springframework.stereotype.Service;
import xyz.shi.entity.User;

@Service
public interface UserService {
int save(User user);
Page<User> findAll();
int update1(User user);
int delete(int id);
}


  • userdao的实现接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package xyz.shi.service.Impl;

import com.github.pagehelper.Page;
import xyz.shi.dao.UserDao;
import xyz.shi.entity.User;
import xyz.shi.service.UserService;

public class UserServiceImpl implements UserService {
// 引用类型
private UserDao userDao;
// 为了使用set注入来赋值,一定要加这个
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public int save(User user) {
return this.userDao.save(user);
}

@Override
public Page<User> findAll() {
return userDao.findAll();
}

@Override
public int update1(User user) {
return userDao.update(user);
}

@Override
public int delete(int id) {
return userDao.delete(id);
}
}

  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
iimport com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xyz.shi.entity.User;
import xyz.shi.service.UserService;

import java.util.Arrays;
import java.util.List;

public class MybatisTest {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
// 其实就是调用的app..xml中的<bean id="userService" class="xyz.shi.service.Impl.UserServiceImpl">
UserService userMybatisService = (UserService) context.getBean("userService");

@Test
public void save() {
User user = new User();
user.setName("test009");
user.setPassword("1234566");
int num = userMybatisService.save(user);
System.out.println(num);
}
@Test
public void findAll() {
//在查询之前,设置分页条件 显示第一页,展示3条数据
Page<Object> page = PageHelper.startPage(1, 3);
List<User> userList = userMybatisService.findAll();
// 设置分页导航数量5
PageInfo<User> pageInfo = new PageInfo<>(userList, 5);
for (User user:userList) {
System.out.println("------每个用户的信息-------");
System.out.println(user.getName());
System.out.println(user.getPassword());
}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}

@Test
public void update() {
User user = new User();
user.setId(41);
user.setName("test41");
user.setPassword("111111");
userMybatisService.update1(user);
}
@Test
public void delete() {
userMybatisService.delete(48);
}
}


总结

  • 注意resources目录的设置要为Resources Root
  • UserDao中的方法一定要和mapper.xml中的id一致
  • 源代码

AOP说明

  • 面向切面编程:基于OOP基础之上新的编程思想,OOP面向的主要对象是类,而AOP面向的主要对象是切面,在处理日志、安全管理、事务管理等方面有非常重要的作用。

  • AOP是Spring中重要的核心点,虽然IOC容器没有依赖AOP,但是AOP提供了非常强大的功能,用来对IOC做补充。

  • 通过Proxy的方式,将重复的公共的代码抽离出去,动态的织入XXService(业务逻辑)中,而不改变原有的Service中的代码结构

AOP的通知类型

  • 前置通知(Before advice): 在连接点之前运行但无法阻止执行流程进入连接点的通知(除非它引发异常)。
  • 后置返回通知(After returning advice):在连接点正常完成后执行的通知(例如,当方法没有抛出任何异常并正常返回时)。
  • 后置异常通知(After throwing advice): 在方法抛出异常退出时执行的通知。
  • 后置通知(总会执行)(After (finally) advice): 当连接点退出的时候执行的通知(无论是正常返回还是异常退出)。
  • 环绕通知(Around Advice):环绕连接点的通知,例如方法调用。这是最强大的一种通知类型,。环绕通知可以在方法调用前后完
  • 自定义的行为。它可以选择是否继续执行连接点或直接返回自定义的返回值又或抛出异常将执行结束。

依赖包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- spring架包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>

实例

  • 本次实例为日志的使用方式

  • 编写dao Calculator接口

1
2
3
4
5
6
7
8
9
package xyz.shi.dao;

public interface Calculator {
public int add(int i,int j);
public int sub(int i,int j);
public int mul(int i,int j);
public int div(int i,int j);
}

  • LogUtil代码,加上Component注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package xyz.shi.util;

import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.stereotype.Component;
@Component
public class LogUtil {
public static void start(Method method, Object ... objects){
System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
}
public static void stop(Method method,Object ... objects){
System.out.println(method.getName()+"方法执行完成,参数是:"+ Arrays.asList(objects));
}

public static void logException(Method method,Exception e){
System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
}
public static void end(Method method){
System.out.println(method.getName()+"方法执行结束了......");
}
}
  • MyCalculatorService代码添加@Service注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package xyz.shi.service;

import org.springframework.stereotype.Service;

@Service(value = "myCalculatorService")
public class MyCalculatorService {
public int add(int i, int j) {
return i + j;
}
public int sub(int i, int j) {
return i - j;
}
public int mul(int i, int j) {
return i * j;
}
public int div(int i, int j) {
return i / j;
}
}
  • 添加自动扫描的配置,resources-application.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描整个目标文件夹 -->
    <context:component-scan base-package="xyz.shi" />
    </beans>
  • 在LogUtil.java中添加@Aspect注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package xyz.shi.util;


import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
@Component
@Aspect
public class LogUtil {
@Before("execution(public int xyz.shi.service.MyCalculatorService.*(int,int))")
public static void start(){
System.out.println("方法开始执行,参数是:");
}
@AfterReturning("execution(public int xyz.shi.service.MyCalculatorService.*(int,int))")
public static void stop(){
System.out.println("方法执行完成,结果是:");

}
@AfterThrowing("execution(public int xyz.shi.service.MyCalculatorService.*(int,int))")
public static void logException(){
System.out.println("方法出现异常:");
}
@After("execution(public int xyz.shi.service.MyCalculatorService.*(int,int))")
public static void end(){
System.out.println("方法执行结束了......");
}
}




  • 开启基于注解的aop的功能,resources-application.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--扫描整个目标文件夹 -->
<context:component-scan base-package="xyz.shi" />
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

  • 执行测试
1
2
3
4
5
6
7
8
9
10
11
12
13
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xyz.shi.dao.Calculator;

public class MyTest {
@Test
public void test01() {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Calculator calculator=context.getBean( "myCalculatorService",Calculator.class);
calculator.add(1, 2);
}
}
  • 结果
1
2
3
4
方法开始执行,参数是:
方法执行完成,结果是:
方法执行结束了......

cglib来创建代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package xyz.shi.service;

import org.springframework.stereotype.Service;

@Service
public class MyCalculatorService {
public int add(int i, int j) {
return i + j;
}

public int sub(int i, int j) {
return i - j;
}

public int mul(int i, int j) {
return i * j;
}

public int div(int i, int j) {
return i / j;
}
}

只需要把我们的MyCalculatorService.java中实现的Calculator.java的接口去掉就行了

  • 测试代码,直接调用service层代码
1
2
3
4
5
6
7
8
9
10
11
12
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xyz.shi.service.MyCalculatorService;

public class MyTest {
@Test
public void test01() {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
MyCalculatorService myCalculator = context.getBean("myCalculatorService", MyCalculatorService.class);
myCalculator.add(1,2);
}
  • 当然也可以不写xml配置文件,直接写在class中,比如
1
2
3
4
5
6
7
8
9
10
11
12
package xyz.shi.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("xyz.shi")
//注意开启AOP的支持,并且代理设置为cglib动态代理,因为UserService没有接口
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}
  • 测试代码需要改变下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import xyz.shi.config.SpringConfig;
import xyz.shi.service.MyCalculatorService;

public class MyTest {
@Test
public void test01() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
MyCalculatorService myCalculator = context.getBean("myCalculatorService", MyCalculatorService.class);
myCalculator.add(1,2);
}
}

xml的AOP配置

  • 这里没有实践
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<context:component-scan base-package="xyz.shi"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<bean id="myCalculatorService" class="xyz.shi.service.MyCalculatorService"></bean>
<bean id="logUtil" class="xyz.shi.util.LogUtil"></bean>

<aop:config>

<aop:aspect ref="logUtil">
<aop:pointcut id="logPoint" expression="execution(public int xyz.shi.service.MyCalculatorService.*(int,int))"/>
<aop:before method="start" pointcut-ref="logPoint"></aop:before>
<aop:after method="end" pointcut-ref="logPoint"></aop:after>
<aop:after-returning method="stop" pointcut-ref="logPoint" returning="result"></aop:after-returning>
<aop:after-throwing method="logException" pointcut-ref="logPoint" throwing="e"></aop:after-throwing>
<aop:around method="myAround" pointcut-ref="logPoint"></aop:around>
</aop:aspect>
</aop:config>
</beans>

切点表达式

  • 切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)

execution

  • execution(public User com.mszlu.service.UserService.findById(int))

  • execution在实际工作中,很少被使用,因为匹配的打击面非常大或者非常小,不能灵活应用

  • execution(public * com.mszlu.service.*.*(..)) 基本能实现无差别全覆盖,即某个包下面的所有Bean的所有方法都会被拦截。

    execution(public * update*(..))从方法的前缀来区分,这种误伤的概率非常大,你不可能要求所有的程序员都按照这种书写习惯来。

annotation

  • 我们使用AOP的时候,常常使用annotation的形式

  • 场景:比如我们实现一个性能监控的需求,使用AOP实现

  1. 定义注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.mszlu.aop;

    import java.lang.annotation.*;

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MsMetric {
    String value() default "";
    }
  2. 在需要监控的方法上,加上注解

    1
    2
    3
    4
    @MsMetric
    public void registerUser(String mail,String password,String nickname){
    //...
    }
  3. 实现性能监控AOP,切点使用annotation

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    package com.mszlu.aop;

    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;

    @Aspect
    @Component
    @Slf4j
    public class MetricAspect {

    // 定义切点 注解所在的方法 即是切点
    @Pointcut("@annotation(MsMetric)")
    public void pt() {}

    //定义通知
    @Around("pt()")
    public Object doLogging(ProceedingJoinPoint pjp) throws Throwable{
    try {
    log.info("----------------metric start--------------------");
    //计算方法执行的时间
    long startTime = System.currentTimeMillis();
    //方法调用
    Object ret = pjp.proceed();
    long endTime = System.currentTimeMillis();
    log.info("方法执行时间:{}ms", endTime-startTime);
    log.info("----------------metric end--------------------");
    return ret;
    }catch (Exception e){
    log.error("异常信息",e);
    throw e;
    }
    }
    }

IoC

  • 控制反转(IoC,Inversion of Control) 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现 方式是依赖注入,比如之前文章调用

  • 依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完 成功能,即 classA 对 classB 有依赖。

  • Ioc 的实现:依赖注入:DI(Dependency Injection),程序代码不做定位查询,这些 工作由容器自行完成。

  • idea新建一个maven-quickstart项目

  • pom.xml中加入依赖文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
</dependencies>
  • service层代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package xyz.shi.service;
import xyz.shi.entity.User;
import java.util.ArrayList;
import java.util.List;

public class UserService {
public List<User> getUserList(){
ArrayList<User> users = new ArrayList<>();
User user1 = new User(1, "test1", "123456");
User user2 = new User(2, "test2", "123456");
User user3 = new User(3, "test3", "123456");
users.add(user1);
users.add(user2);
users.add(user3);
return users;
}
}

  • resources/application.xml
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="xyz.shi.service.UserService"></bean>
</beans>
1
2
3
4
<!--单例,默认是单例,可以不写--> 
<bean id="mailService" name="mailService2" class="com.mszlu.service.MailService" scope="singleton"/>
<!--非单例,每次获取都是一个新的实例,不常用-->
<bean id="mailService" name="mailService2" class="com.mszlu.service.MailService" scope="prototype"/>
  • 测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import xyz.shi.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xyz.shi.service.UserService;

public class userTest {
public static void main(String[] arg) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
// UserService userService =(UserService) context.getBean("userService");
UserService userService1 = context.getBean(UserService.class);
for (User user : userService1.getUserList()) {
System.out.println(user.getName());
}
}
}

ioc注解

  • 使用XML方式管理Bean以及Bean的依赖,非常直观,但是配置相对繁琐一些,尤其是当Bean多了之后,有没有更简便的方式呢?
  • Annotation配置使用注解的方式,可以极大的简化配置
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="xyz.shi.service" />
</beans>
  • 件扫描的作用: 让Spring知道该去哪个包中扫描注解, 以及扫描什么样的注解, 其中用到的标签就是<context:component-scan>, 其属性base-package指定扫描哪个包下的类, 多个包可以用逗号隔开, 比如:
1
<context:component-scan base-package="com.yjzzjy4.learning.beans, com.yjzzjy4.learning.test"/>
  • 关于扫描什么样的注解, 可以使用过滤器定义规则
1
2
3
<context:component-scan base-package="com.yjzzjy4.learning" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>

或者示例二:

1
2
3
<context:component-scan base-package="com.yjzzjy4.learning">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
  • 其中<context:component-scan>的use-default-filters属性指定是否使用Spring默认的过滤器, 是一个布尔属性, 默认为true, 即扫描所有注解. 当设为false的时候, 则需要自己配置扫描规则, 通常用<context:include-filter>来包含要扫描的注解类型(示例一), 如果使用默认过滤器, 则通常会使用<context:exclude-filter>注解来排除掉不想要扫描的注解类型(示例二), 其中:
    • org.springframework.stereotype.Component: 即@Component注解;
    • org.springframework.stereotype.Service:即@Service注解;
  • 而type属性值设为annotation即说明过滤器的作用对象是注解.

创建对象

Spring提供了以下四个注解用于创建对象:

  • @Component: 建议用于普通Bean;
  • @Controller: 建议用在Web层的Controller;
  • @Repository: 建议用在DAO层的仓储Bean;
  • @Service: 建议用在Service层的Bean.
  • 实际上这四个注解都是实现相同的功能, Spring将它们区分开来仅仅是为了应用开发的逻辑清晰, 并没有硬性规定这几个注解的使用场合, 这些注解都有一个属性: value, 用于指定Bean的id, 即:
1
2
@Component(value = "employee")
值得注意的是, 默认情况下若不给value设置值, 则value自动取类名首字母小写为值
  • 等价于:
1
<bean id="employee" .../>

练习

resources-application.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描整个目标文件夹 -->
<context:component-scan base-package="xyz.shi" />
</beans>
  • controller注解
1
2
3
4
5
6
7
package xyz.shi.controller;
import org.springframework.stereotype.Controller;

@Controller
public class PersonController {
}

  • Repository 注解dao
1
2
3
4
5
package xyz.shi.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
}
  • Service 注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package xyz.shi.service;
import xyz.shi.entity.User;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public static List<User> getUserList(){
ArrayList<User> users = new ArrayList<>();
User user1 = new User(1, "test1", "123456");
User user2 = new User(2, "test2", "123456");
User user3 = new User(3, "test3", "123456");
users.add(user1);
users.add(user2);
users.add(user3);
return users;
}
}
  • Component注解实体类
1
2
3
4
5
6
7
8
9
10
package xyz.shi.entity;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component(value = "userBean")
@Scope(value = "prototype")
public User () {

}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class userTest {
public static void main(String[] arg) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
System.out.println(context.getBean("personController"));
System.out.println(context.getBean("userService"));
System.out.println(context.getBean("userDao"));
System.out.println(context.getBean("userBean"));
System.out.println(context.getBean("role"));
}

}
  • 结果如下
1
2
3
4
5
xyz.shi.controller.PersonController@1cab0bfb
xyz.shi.service.UserService@5e955596
xyz.shi.dao.UserDao@50de0926
xyz.shi.entity.User@2473b9ce
xyz.shi.entity.Role@60438a68

注入属性

IoC的第二个任务就是注入属性, Spring提供了三个注解用于完成此任务:

  • @Autowired: 按类型进行自动装配, 不可以有多个相同类型的Bean;
  • @Qualifier: 按名称进行装配, 要和@Autowired一起使用;
  • @Value: 注入值类型属性.
  • 还有一个注解是由Java提供的:
    • @Resource: 既可以根据类型, 也可以根据名称注入, 相当于@Autowired@Qualifier.
    • 通常建议使用Spring提供的注解, 因为在以后的Spring版本迭代中将会更可控一些.

使用@AutoWired进行自动注入

  • dao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package xyz.shi.dao;
import org.springframework.stereotype.Repository;
import xyz.shi.entity.User;

import java.util.ArrayList;
import java.util.List;

@Repository
public class UserDao {
public List<User> getUserList(){
ArrayList<User> users = new ArrayList<>();
User user1 = new User(1, "test1", "123456");
User user2 = new User(2, "test2", "123456");
User user3 = new User(3, "test3", "123456");
users.add(user1);
users.add(user2);
users.add(user3);
return users;
}
}

  • service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package xyz.shi.service;
import org.springframework.beans.factory.annotation.Autowired;
import xyz.shi.dao.UserDao;
import xyz.shi.entity.User;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserDao userDao;
public List<User> getUserList(){
return userDao.getUserList();
}
}

Autowired 加入后,不用在实例化了

  • controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package xyz.shi.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import xyz.shi.entity.User;
import xyz.shi.service.UserService;

import java.util.List;

@Controller
public class UserController {
@Autowired
private UserService userService;
public List<User> getUserList() {
return userService.getUserList();
}
}

  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xyz.shi.controller.UserController;
import xyz.shi.entity.User;

import java.util.List;

public class userTest {
public static void main(String[] arg) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserController userController = context.getBean("userController", UserController.class);
List<User> users = userController.getUserList();
for(User user: users) {
System.out.println(user.getName());
}
}
}

@Qualifier注解

  • 修改UserControll.java加上@Qualifer注解,指定id为userService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package xyz.shi.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import xyz.shi.entity.User;
import xyz.shi.service.UserService;

import java.util.List;

@Controller
public class UserController {
@Autowired
@Qualifier("userService")
private UserService userService111;
public List<User> getUserList() {
return userService111.getUserList();
}
}

这样运行测试代码是没有问题,如果是@Qualifier(“userService1”),就会报错,因为找不到对应的name

  • AutoWired可以进行定义在方法上
1
2
3
4
@Autowired
public void test(PersonDao personDao){
System.out.println("personDao:"+personDao);
}
  • @Qualifier注解也可以作用在属性上,用来被当作id去匹配容器中的对象,如果没有 此注解,那么直接按照类型进行匹配
1
2
3
public void test1(@Qualifier("personServiceSon") PersonService personService){
personService.getPerson();
}

说明

  • 上篇文章 主要用jsp+jdbc的方式进行了练习,本次采用ORM框架mybatis代替jdbc,实现增删改查

  • java版本为1.8

  • 本次练习源代码

配置

  • pom.xml中加入依赖文件
1
2
3
4
5
6
7
8
9
10
 <dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
  • 新增一个实例类 src-main-java-xyz.shi.entity.User
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package xyz.shi.entity;
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public User () {

}
public User(int id,String name, String password) {
this.name = name;
this.id = id;
this.password = password;
}

public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}

}
  • 接下来就要创建一个xml文件作为MyBatis的核心配置文件了,src-main-resources-mybatis.config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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>

<!-- 注意:environments标签,当mybatis和spring整合之后,这个标签是不用配置的 -->

<!-- 可以配置多个运行环境,但是每个 SqlSessionFactory 实例只能选择一个运行环境
一、development:开发模式
二、work:工作模式-->
<environments default="development">
<!--id属性必须和上面的default一样 -->
<environment id="development">
<!--事务管理器
一、JDBC:这个配置直接简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围
二、MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期
比如 spring 或 JEE 应用服务器的上下文,默认情况下,它会关闭连接。然而一些容器并不希望这样,
因此如果你需要从连接中停止它,就可以将 closeConnection 属性设置为 false,比如:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
-->
<transactionManager type="JDBC"/>
<!--dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>

</configuration>

替换连接信息解决硬编码问题

  • 编写sql映射文件了,src-main-resources-UserMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?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="usersMapper">

<!-- 根据 id 查询 users 表中的数据
id:唯一标识符,此文件中的id值不能重复
resultType:返回值类型,一条数据库记录也就对应实体类的一个对象
parameterType:参数类型,也就是查询条件的类型
-->
<select id="selectusersById"
resultType="xyz.shi.entity.User" parameterType="int">
<!-- 这里和普通的sql 查询语句差不多,对于只有一个参数,后面的 #{id}表示占位符,里面不一定要写id,写啥都可以,但是不要空着,如果有多个参数则必须写pojo类里面的属性 -->
select * from users where id = #{id}
</select>


<!-- 查询 users 表的所有数据
注意:因为是查询所有数据,所以返回的应该是一个集合,这个集合里面每个元素都是users类型
-->
<select id="selectusersAll" resultType="xyz.shi.entity.User">
select * from users
</select>

<!-- 模糊查询:根据 users 表的usersname字段
下面两种写法都可以,但是要注意
1、${value}里面必须要写value,不然会报错
2、${}表示拼接 sql 字符串,将接收到的参数不加任何修饰拼接在sql语句中
3、使用${}会造成 sql 注入
-->
<select id="selectLikeusersName" resultType="xyz.shi.entity.User" parameterType="String">
select * from users where name like '%${value}%'
<!-- select * from users where usersname like #{usersname} -->
</select>

<!-- 向 users 表插入一条数据 -->
<insert id="insertusers" parameterType="xyz.shi.entity.User">
insert into users(name,password)
value(#{name},#{password})
</insert>

<!-- 根据 id 更新 users 表的数据 -->
<update id="updateusersById" parameterType="xyz.shi.entity.User">
update users set name=#{name},password=#{password} where id=#{id}
</update>

<!-- 根据 id 删除 users 表的数据 -->
<delete id="deleteusersById" parameterType="int">
delete from users where id=#{id}
</delete>
</mapper>

统一管理sql语句,解决硬编码问题

  • src-main-resources-mybatis-config.xml 配置文件中注册 userMapper.xml 文件
1
2
3
4
5
6
...
</environment>
<mappers>
<!-- 注册userMapper.xml文件 -->
<mapper resource="UserMapper.xml"/>
</mappers>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package xyz.shi;

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 xyz.shi.entity.User;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class CRUDTest {
public static void main(String[] args) throws IOException {
//1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 执行sql (只有这块需要手写,其他步骤直接复制)
List<User> users = sqlSession.selectList("usersMapper.selectusersAll"); // 命名空间+id,这就是命名空间的作用:便于区分
// System.out.println(users);
for(User user: users) {
System.out.println(user.getId());
System.out.println(user.getName());
System.out.println(user.getPassword());
}

// 插入
User user1 = new User();
user1.setName("haha");
user1.setPassword("111111");
sqlSession.insert("usersMapper.insertusers", user1);
sqlSession.commit();

// 修改
User user2 = new User();
user2.setId(1);
user2.setName("haha");
user2.setPassword("111111");
sqlSession.update("usersMapper.updateusersById", user2);
sqlSession.commit();

//删除
User user3= new User();
user3.setId(24);
sqlSession.delete("usersMapper.deleteusersById", user3);
sqlSession.commit();


sqlSession.close();
}

}

优化架构

mybatis-config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?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>

<!--读取配置文件中的数据库连接信息-->
<properties resource="db.properties"/>
<settings>
<!--设置日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启驼峰命名,映射数据库到对象属性名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>

<!--给实体类取别名,为了后面配置mapper.xml时不用写全限定名,方便使用-->
<typeAliases>
<!--包扫描-->
<package name="xyz.shi.entity"/>
</typeAliases>

<environments default="development">
<!--id属性必须和上面的default一样 -->
<environment id="development">
<!--事务管理器
一、JDBC:这个配置直接简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围
二、MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期
比如 spring 或 JEE 应用服务器的上下文,默认情况下,它会关闭连接。然而一些容器并不希望这样,
因此如果你需要从连接中停止它,就可以将 closeConnection 属性设置为 false,比如:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
-->
<transactionManager type="JDBC"/>
<!--dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象源 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 注册userMapper.xml文件 -->
<mapper resource="UserMapper.xml"/>
</mappers>

</configuration>

UserMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?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="usersMapper">

<!-- 根据 id 查询 users 表中的数据
id:唯一标识符,此文件中的id值不能重复
resultType:返回值类型,一条数据库记录也就对应实体类的一个对象
parameterType:参数类型,也就是查询条件的类型
-->

<!--通过id(唯一)获取用户-->
<select id="queryById" parameterType="int" resultType="User">
select * from users where id = #{id};
</select>

<select id="queryOne" resultType="User" parameterType="map">
select * from users where name = #{name} and password=#{password};
</select>
<!-- <select id="getUserList" parameterType="map" resultType="User">-->
<select id="getUserList" resultType="User">
select * from users
<!-- <if test="name != null">-->
<!-- and name = #{name}-->
<!-- </if>-->
<!-- <if test="password != null">-->
<!-- and password = #{password}-->
<!-- </if>-->
<!-- <trim prefix="limit">-->
<!-- <if test="curPage != null and pageSize != null">-->
<!-- #{curPage},#{pageSize}-->
<!-- </if>-->
<!-- </trim>-->
</select>

<!-- 向 users 表插入一条数据 -->
<insert id="addUser" parameterType="User">
insert into users(name,password)
value(#{name},#{password})
</insert>

<update id="modify" parameterType="User">
update users set name=#{name},password=#{password} where id=#{id}
</update>

<!-- 根据 id 删除 users 表的数据 -->
<delete id="deleteById" parameterType="int">
delete from users where id=#{id}
</delete>
</mapper>

三层架构

实体层(entity)

  • User.java 代码省略

数据访问层(Dao)

  • 接口类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package xyz.shi.dao;

import xyz.shi.entity.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;

public interface UserDao {
List<User> getUserList();
User queryOne(User user);
User queryById(int id);
// //通过id删除用户
int deleteById(@Param("id")int id);
int addUser(User user);

int modify(User user);
}

  • 实现接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package xyz.shi.dao.impl;

import org.apache.ibatis.session.SqlSession;
import xyz.shi.dao.UserDao;
import xyz.shi.entity.User;

import java.util.List;
import java.util.Map;
import xyz.shi.utils.MybatisUtils;
public class UserDaoImpl implements UserDao {

@Override
public List<User> getUserList() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 命名空间+id,这就是命名空间的作用:便于区分
List<User> users = sqlSession.selectList("usersMapper.getUserList");
sqlSession.close();
return users;
}

@Override
public User queryOne(User user) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
List<User> users = sqlSession.selectList("usersMapper.queryOne", user);
sqlSession.close();
if (users != null) {
return users.get(0);
} else {
return null;
}
}

@Override
public User queryById(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
User user1 = sqlSession.selectOne("usersMapper.queryById", id);
sqlSession.close();
return user1;
}

@Override
public int deleteById(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.delete("usersMapper.deleteById", id);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int addUser(User user) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("usersMapper.addUser", user);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int modify(User user) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("usersMapper.modify", user);
sqlSession.commit();
sqlSession.close();
return num;
}
}

业务逻辑(services)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package xyz.shi.service;

import xyz.shi.entity.User;

import java.util.List;
public interface UserService {
List<User> getUserList();
User queryOne(User user);
User queryById(int id);
int deleteById(int id);
int addUser(User user);
int modify(User user);

}

  • 实现接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package xyz.shi.service.impl;

import xyz.shi.dao.UserDao;
import xyz.shi.dao.impl.UserDaoImpl;
import xyz.shi.entity.User;
import xyz.shi.service.UserService;
import java.util.List;

public class UserServiceImpl implements UserService {
//创建UserDaoImpl对象
private UserDao userDao = new UserDaoImpl();

@Override
public List<User> getUserList() {
return userDao.getUserList();
}

@Override
public User queryOne(User user) {
return userDao.queryOne(user);
}

@Override
public User queryById(int id) {
return userDao.queryById(id);
}

@Override
public int deleteById(int id) {
return userDao.deleteById(id);
}

@Override
public int addUser(User user) {
return userDao.addUser(user);
}

@Override
public int modify(User user) {
return userDao.modify(user);
}
}

servlet层

  • 接受jsp数据,传送数据到jsp页面,比如下面的用户详情页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package xyz.shi.servlet;

import xyz.shi.entity.User;
import xyz.shi.service.UserService;
import xyz.shi.service.impl.UserServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/userFind")
public class FindUserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
UserService userService = new UserServiceImpl();
int userId = Integer.parseInt(req.getParameter("id"));
User user = userService.queryById(userId);
if (user != null) {
req.setAttribute("user", user);
req.getRequestDispatcher("find.jsp").forward(req, resp);
} else {
// 登录失败,返回登录页面
System.out.println("查找数据失败");
req.setAttribute("message", "failed");

}
}
}

view层

webapp/下面的login、add、find等jsp文件,用户详情页,并进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>添加用户</title>
</head>
<body>
<div><a href="login">主页</a></div>
<form action="userUpdate" method="post">
用户名:<br>
<input type="text" name="username" value="${user.getName()}">
<br>
密码:<br>
<input type="password" name="password" value="${user.getPassword()}" >
<input type="hidden" name="id" value="${user.getId()}">
<br><br>
<input type="submit" value="提交" >
</form>

</body>
</html>

页面

image-20231212113144060