运行效果
点击录入,下方的状态会变成数据更新成功。
刷新student_db数据库,发现学生数据被录入数据库并成功获取展示到花名册页面。
运行演示完,写一下这个小项目的步骤。
新建数据库student_db,在该数据库下新建表stu_tb
CREATE TABLE `stu_tb` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`major` varchar(128) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`gender` varchar(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
新建SpringBoot项目
配置项目
打开项目根目录下的pom.xml文件,配置项目基本和其他依赖。
依赖:如果报红需要reimport
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
打开resource目录下的application.properties文件,开始配置数据库信息。
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/student_db?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=你的数据库用户名,默认root
spring.datasource.password=你的数据库密码
开始写后端代码
model
新建一个包,model,在model包下新建一个类Student,
在写好类的私有属性后,快捷键alt+insert自动生成getter 和setter方法,自动生成重写toString方法。加上注解,自动导包。
最后,你的Student.java代码如下:
package com.liulei.stuinfo.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.LocalDate;
@Entity
@Table(name = "stu_tb")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;
@Column
private String name;
@Column
private String major;
@Column
private LocalDate birthday;
@Column
private String gender;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", major='" + major + '\'' +
", birthday=" + birthday +
", gender='" + gender + '\'' +
'}';
}
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 String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
public LocalDate getBirthday() {
return birthday;
}
public void setBirthday(LocalDate birthday) {
this.birthday = birthday;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
dao
StudentDAO.java
package com.liulei.stuinfo.dao;
import com.liulei.stuinfo.model.Student;
import java.util.List;
public interface StudentDAO {
List<Student> get();
Student get(int id);
void save(Student student);
void delete(int id);
}
StudentDAOImpl.java
package com.liulei.stuinfo.dao;
import com.liulei.stuinfo.model.Student;
import org.hibernate.Session;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import java.util.List;
@Repository
public class StudentDAOImpl implements StudentDAO{
@Autowired
private EntityManager entityManager;
@Override
public List<Student> get() {
Session currSession = entityManager.unwrap(Session.class);
Query<Student> query = currSession.createQuery("from Student", Student.class);
List<Student> list = query.getResultList();
return list;
}
@Override
public Student get(int id) {
Session currSession = entityManager.unwrap(Session.class);
Student stu = currSession.get(Student.class, id);
return stu;
}
@Override
public void save(Student Student) {
Session currSession = entityManager.unwrap(Session.class);
currSession.saveOrUpdate(Student);
}
@Override
public void delete(int id) {
Session currSession = entityManager.unwrap(Session.class);
Student stu = currSession.get(Student.class, id);
currSession.delete(stu);
}
}
service
StudentService.java
package com.liulei.stuinfo.service;
import com.liulei.stuinfo.model.Student;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface StudentService {
@Transactional
List<Student> get();
@Transactional
Student get(int id);
@Transactional
void save(Student student);
@Transactional
void delete(int id);
}
StudentServiceImpl.java
package com.liulei.stuinfo.service;
import com.liulei.stuinfo.dao.StudentDAO;
import com.liulei.stuinfo.model.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class StudentServiceImpl implements StudentService{
@Autowired
private StudentDAO studentDao;
@Transactional
@Override
public List<Student> get() {
return studentDao.get();
}
@Transactional
@Override
public Student get(int id) {
return studentDao.get(id);
}
@Transactional
@Override
public void save(Student student) {
studentDao.save(student);
}
@Transactional
@Override
public void delete(int id) {
studentDao.delete(id);
}
}
controller
StudentController.java
package com.liulei.stuinfo.controller;
import com.liulei.stuinfo.model.Student;
import com.liulei.stuinfo.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api")
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/student")
public List<Student> get() {
return studentService.get();
}
@PostMapping("/student")
public Student save(@RequestBody Student student) {
studentService.save(student);
return student;
}
@GetMapping("/student/{id}")
public Student get(@PathVariable int id) {
return studentService.get(id);
}
@DeleteMapping("/student/{id}")
public String delete(@PathVariable int id) {
studentService.delete(id);
return "Student removed with id "+id;
}
@PutMapping("/student")
public Student update(@RequestBody Student student) {
studentService.save(student);
return student;
}
}
StuinfoApplication.java无需修改
接着开始写前端代码-React
在工作区目录执行create-react-app stuinfo创建React项目
cd stuinfo进入到项目根目录,
再在components建立AddStudent.js用以录入学生信息的界面,本项目前端样式用的@material-ui,
需要提前添加到依赖中:yarn add
yarn add @material-ui/core
yarn add @material-ui/icons
import React from "react";
import 'isomorphic-fetch';
import Avatar from "@material-ui/core/Avatar";
import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline";
import TextField from "@material-ui/core/TextField";
import { Link } from "react-router-dom";
import Grid from "@material-ui/core/Grid";
import GroupIcon from "@material-ui/icons/Group";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
const useStyles = makeStyles(theme => ({
paper: {
marginTop: theme.spacing(7),
display: "flex",
flexDirection: "column",
alignItems: "center"
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main
},
form: {
width: "100%",
marginTop: theme.spacing(3)
},
submit: {
margin: theme.spacing(3, 0, 2)
},
textField: {
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
width: "100%"
}
}));
export default function AddStudent() {
const classes = useStyles();
const [firstLoad, setLoad] = React.useState(true);
const [selectedDate, setSelectedDate] = React.useState(
new Date("1999-12-12T21:11:11")
);
const [name, setName] = React.useState("");
const [major, setMajor] = React.useState("");
const [gender, setGender] = React.useState("");
const handleDateChange = date => setSelectedDate(date.target.value);
const handleNameChange = event => setName(event.target.value);
const handleMajorChange = event => setMajor(event.target.value);
const handleGenderChange = event => setGender(event.target.value);
const [message, setMessage] = React.useState("暂未保存任何内容");
async function sampleFunc(toInput) {
const response = await fetch("/api/student", {
method: "POST",
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json"
},
redirect: "follow",
referrerPolicy: "no-referrer",
body: JSON.stringify(toInput) header
});
let body = await response.json();
console.log(body.id);
setMessage(body.id ? "数据更新成功" : "数据更新失败");
}
const handleSubmit = variables => {
const toInput = { name, major, gender, birthday: selectedDate };
sampleFunc(toInput);
setName("");
setMajor("");
setGender("");
};
if (firstLoad) {
// sampleFunc();
setLoad(false);
}
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<GroupIcon />
</Avatar>
<Typography component="h1" variant="h5">
学生花名册
</Typography>
<form className={classes.form} noValidate>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="name"
value={name}
label="姓名"
name="name"
autoComplete="name"
onChange={handleNameChange}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
autoComplete="major"
name="major"
variant="outlined"
required
fullWidth
value={major}
id="major"
label="专业"
onChange={handleMajorChange}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
variant="outlined"
required
fullWidth
id="gender"
value={gender}
label="性别"
name="gender"
autoComplete="gender"
onChange={handleGenderChange}
/>
</Grid>
<Grid item xs={12}>
<TextField
id="date"
label="生日"
type="date"
defaultValue="1999-12-12"
className={classes.textField}
InputLabelProps={{
shrink: true
}}
onChange={handleDateChange}
/>
</Grid>
</Grid>
<Button
// type="submit"
fullWidth
variant="contained"
color="primary"
preventDefault
className={classes.submit}
onClick={handleSubmit}
>
录入
</Button>
<Grid container justify="center">
<Grid item>
<Link to="/view">查看学生花名册</Link>
</Grid>
</Grid>
</form>
<Typography style={{ margin: 7 }} variant="body1">
状态: {message}
</Typography>
</div>
</Container>
);
}
再在components建立Table.js用以输出显示学生花名册
import React from "react";
import 'isomorphic-fetch';
import { makeStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import Avatar from "@material-ui/core/Avatar";
import GroupIcon from "@material-ui/icons/Group";
import { Link } from "react-router-dom";
import Typography from "@material-ui/core/Typography";
import CircularProgress from "@material-ui/core/CircularProgress";
const useStyles = makeStyles(theme => ({
table: {
minWidth: 600
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main
},
paper: {
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
margin: `10px`,
height: "100%",
width: "99%",
marginTop: theme.spacing(7)
},
link: {
color: "rgba(0,0,0,0.65)",
textDecoration: "none",
marginLeft: "10%",
alignSelf: "flex-start",
"&:hover": {
color: "rgba(0,0,0,1)"
}
}
}));
export default function SimpleTable() {
const classes = useStyles();
const [data, upDateData] = React.useState([]);
const [firstLoad, setLoad] = React.useState(true);
let isLoading = true;
async function sampleFunc() {
let response = await fetch("http://localhost:8081/api/student");
let body = await response.json();
upDateData(body);
}
if (firstLoad) {
sampleFunc();
setLoad(false);
}
if (data.length > 0) isLoading = false;
return ((
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<GroupIcon />
</Avatar>
<Typography component="h1" variant="h5">
学生花名册
</Typography>
{isLoading ? (
<CircularProgress />
) : (
<TableContainer
style={{ width: "80%", margin: "0 10px" }}
component={Paper}
>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell align="center">姓名</TableCell>
<TableCell align="center">专业</TableCell>
<TableCell align="center">性别</TableCell>
<TableCell align="center">生日</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data?.map(row => (
<TableRow key={row.name}>
<TableCell align="center">{row.name}</TableCell>
<TableCell align="center">{row.major}</TableCell>
<TableCell align="center">{row.gender}</TableCell>
<TableCell align="center">{row.birthday}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
<Link className={classes.link} to="/">
{" "}
<Typography align="left">
← 返回录入界面
</Typography>{" "}
</Link>
</div>
) );
}
再在App.js中引入上述组件
import React, { Component } from "react";
import AddStudent from "./components/AddStudent";
import { Route,Routes, BrowserRouter as Router } from "react-router-dom";
import Table from "./components/Table";
class App extends Component {
render() {
return (
<Router>
<Routes>
<Route exact path="/" componet=<AddStudent/> />
<Route exact path="/view" componet=<Table/> />
</Routes>
</Router>
);
}
}
export default App;
最后在index.js中引入App
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<App />,
document.getElementById('root')
);
最后在项目根目录执行yarn start就可以启动项目了。在此之前需要先启动springboot的主程序类
StuinfoApplication