- 项目简介
- 项目功能
- 项目技术
- 项目结构
- 项目内容
- 1.数据库设计
- 2.项目及idea的配置
- 3.创建entity包(实体包)
- 4.根据功能介绍,确定请求和响应
- 5.封装数据库的相关操作
- 6.实现Servlet
- 7.前端页面设计
- 8.最终实现效果
项目简介
基于Java Servlet 构建的在线音乐服务器。
项目功能
- 登录、注册
- 上传音乐
- 删除某一个音乐信息
- 删除选中的音乐信息
- 查询音乐(包含查找指定/模糊匹配的音乐)
- 添加音乐到“喜欢列表”。
- 查询喜欢的音乐(包含查找指定/模糊匹配的音乐)
项目技术
- 前端知识:HTML+CSS+JavaScript
- 后端:Servlet 的使用
- 数据库:mysql
- tomcat、Maven的使用
- json 的使用
项目结构
项目整体基于HTTP协议。
前端使用HTML+CSS+JS构建页面整体布局。
后端采用分层结构,分为Servlet层,Dao层。
采用分层设计,降低了整个代码的耦合度。
MySQL由Dao层操作再返回给Dao层。
Dao层由Servlet操作,再返回。
前端页面和Servlet进行交互。
项目内容
1.数据库设计
当前需要三张表。
• 创建数据库
drop database if exists musicplayer;
create database if not exists musicplayer character set utf8;
--使用数据库
use musicplayer;
• 创建表
user表
drop table if exists user;
create table user (
id int primary key auto_increment,
username varchar(20) not null,
password varchar(30) not null,
age int not null,
sex varchar(2) not null,
email varchar(50) not null
);
music表
drop table if exists music;
create table music (
id int primary key auto_increment,
title varchar(50) not null,
singer varchar(30) not null,
time varchar(13) not null,
url varchar(100) not null,
userid int(11) not null
);
lovemusic表
drop table if exists lovemusic;
create table lovemusic (
id int primary key auto_increment,
user_id int(11) not null,
music_id int(11) not null
);
-- 可插入一条用户信息
insert into user(username,password,age,sex,email)
values("lala","2233","12","女","965543061@qq.com");
2.项目及idea的配置
1)使用maven打包。
在 pom.xml 指定当前打包打成 war 包。
2)创建结构,生成 web.xml。
main文件夹下,NewFile --> webapp/ WEB-INF/ web.xml
3)设置编码格式,防止出现中文乱码。
4)设置自动编译。
5)配置 pom.xml
向 maven 中引入相关依赖。
pom.xml 文件 中放Jackson、servlet、 mysql相关的依赖。
再指定当前 war包的名字:
<finalName>OnlineMusic</finalName>
<!-- 注意:指定的war包的名称是之后项目运行时的 根目录 -->
再指定一些插件的版本,以免受到 maven 版本的影响。
3.创建entity包(实体包)
根据数据库创建实体类:User、Music。其中的属性与数据库中的对应。
User类
package entity;
public class User {
private int id;
private String username;
private String password;
private int age;
private String sex;
private String email;
}
Music类
package entity;
public class Music {
private int id;
private String title;
private String singer;
private String time;
private String url;
private int userId;
}
4.根据功能介绍,确定请求和响应
使用Json
Json 是一种键值对风格的数据格式, 把前端输入的对象转换成字符串(序列化)。
Java中 我们采用Jackson库中的 ObjectMapper类来完成Json的构造。
向 maven 中引入依赖:
<!-- Jackson相关-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.4</version>
</dependency>
1)登录:
请求:
POST:/loginServlet
data:{username,password}响应:{msg:true}
2)注册:
请求:
POST:/register
data:{username,password,age,sex,email}响应:{msg:true}
3)上传音乐:
请求:
POST:/upload
data:filename
4)删除某一个音乐:
请求:
POST:/deleteServlet
data:{“id”:id}响应:{msg:true}
5)删除选中:
请求:(id 数组)
POST:/deleteSelMusic
data:{“id”:id}响应:{msg:true}
6)模糊查询音乐:
请求:
Get:/findMusic
data:{musicName:musicName}响应:{msg:true}
7)添加喜欢音乐到喜欢列表:
请求:
POST:/loveMusic
data:{“id”:obj}响应:{msg:true}
8)模糊查询喜欢的音乐:
请求:
Get:/findLoveMusic
data:{musicName:musicName}响应:{msg:true}
9)移除喜欢的音乐:
请求:
POST:/removeLoveMusic
data:{‘id’:obj}响应:{msg:true}
5.封装数据库的相关操作
1)创建 util 包(工具相关的),其中再创建 DBUtils 类,和数据库建立连接。
public class DBUtils { // 数据库名字对应
private static String url = "jdbc:mysql://127.0.0.1:3306/数据库名称?useSSL=false";
private static String password ="2222";//数据库密码
private static String username = "root";
private static volatile DataSource dataSource;
// 获取数据源
private static DataSource getDataSource(){
// 双重校验锁
if(dataSource == null){
synchronized (DBUtils.class){
if(dataSource == null){
dataSource = new MysqlDataSource();
((MysqlDataSource) dataSource).setUrl(url);
((MysqlDataSource) dataSource).setUser(username);
((MysqlDataSource) dataSource).setPassword(password);
}
}
}
return dataSource;
}
// 获取连接
public static Connection getConn(){
try {
Connection connection = getDataSource().getConnection();
return connection;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("获取数据库连接失败");
}
}
// 关闭数据库 存在依赖关系,关闭和连接顺序相反
public static void getClose(Connection connection, PreparedStatement statement,
ResultSet resultSet) {
if(resultSet!=null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement!=null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2)创建 dao 包(数据库持久层),主要来操作MySQL,实现各功能。
创建UserDao类:
login;
register
创建MusicDao类:
insert;
findMusic;
findMusicById;
findMusicByKey;
deleteMusicById;
findLoveMusicById;
deleteLoveMusicById
创建LoveMusicDao类:
findLoveMusic;
findLoveMusicByMusicIdAndUserId;
findLoveMusicBykeyAndUID;
removeLoveMusic
注意:
1.查询当前用户喜欢的音乐,数据库中需要两张表联合查询 music、musicLove。
public List<Music> findLoveMusic(int user_id) {
List<Music> musicList = new ArrayList<>();
Connection con = null;
PreparedStatement prs = null;
ResultSet resultSet = null;
try {
con = DBUtils.getConn();
// String sql ="select * from lovemusic where user_id =?";
// 查询 user_id ,数据在 music表里,需要两张表联合查询
// music: id title singer time url userid
// lovemusic: id user_id music_id
String sql = "select m.id as m_id,title,singer,time,url,userid from" +
" music m,lovemusic lm where m.id = lm.music_id and lm.user_id =?";
prs = con.prepareStatement(sql); // 执行完 不需要插入
prs.setInt(1,user_id); // 给个指定的值
resultSet = prs.executeQuery(); // 然后 判断结果集 显示
while (resultSet.next()){
Music music = new Music();
music.setId(resultSet.getInt("m_id"));
music.setTitle(resultSet.getString("title"));
music.setSinger(resultSet.getString("singer"));
music.setTime(resultSet.getString("time"));
music.setUrl(resultSet.getString("url"));
music.setUserId(resultSet.getInt("userid"));
musicList.add(music);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.getClose(con,prs,resultSet);
}
return musicList;
}
6.实现Servlet
实现各个Servlet,处理请求。
Servlet与前端页面交互,生成动态Web页面。
例:登录loginServlet:
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8"); // 设置编码,设置响应数据格式,拿到数据,通过dao层,操作数据库,最后返回响应的数据,再返回给前端
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("username:"+username);
System.out.println("password:"+password);
Map<String ,Object> return_map = new HashMap<>();
// service层和dao层联系,又需要 dao层操作数据库
UserDao userDao = new UserDao();
// 登录需要登录用户 loginUser
User loginUser = new User(); //创建一个数据库实体类对象
loginUser.setUsername(username);
loginUser.setPassword(password);
LoginService loginService = new LoginService();
User user = loginService.login(loginUser);
// User user = userDao.login(loginUser); // 接受下登录的返回值
if (user == null){
// 登录失败
return_map.put("msg",false);
}else {
req.getSession().setAttribute("user", user);//绑定数据
return_map.put("msg",true);
}
ObjectMapper mapper = new ObjectMapper();
//利用Jackson将map转化为json对象,
// writer 将转换后的json字符串保存到字符输出流中,最后给客户端
mapper.writeValue(resp.getWriter(),return_map); // 写到前端
}
}
注意:
上传音乐的流程:
1)将音乐上传到服务器
2)再将音乐信息存入数据库
@WebServlet("/upload")
@MultipartConfig
public class UploadMusicServlet extends HttpServlet {
// 定义文件存储位置,上传到 music 文件夹
private static final String SAVEPATH = "D:\\IDEA\\IdeaProjects\\OnlineMusic\\src\\main\\webapp\\music\\";
private static final String SAVEPATH = "/home/liu/install/apache-tomcat-8.5.69/webapps/OnlineMusic/music/"; // 部署到云服务器 对应路径
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("application/json;charset=utf-8");
User user =(User) req.getSession().getAttribute("user"); // 需要从请求中拿到刚刚登录后的session;
// 此处attribute()中取的要和登录时的key--》user对应
if (user == null){
System.out.println("没有登录,不能上传!");
}else {
//1、先上传服务器
Part part = req.getPart("filename"); // fiddler 抓包获取到的内容 Content-Disposition:
// form-data; name="filename"; filename="毛不易 - 若有缘由.mp3";
//从content-disposition头中获取源文件名称,获取到一个字符串
String header = part.getHeader("Content-Disposition");
// 字符串处理
int start = header.lastIndexOf("="); // 最后一个 = 的后面 ,就是需要的内容
String fileName = header.substring(start + 1) .replace("\"", ""); // 把 " 替换掉
// 最后获取到的应该是:毛不易 - 若有缘由.mp3
System.out.println("fileName2:" + fileName);
// 现在已经拿到音乐文件.mp3 --- 写到服务器指定路径下
part.write(SAVEPATH + fileName); // 调用 write方法,在SAVEPATH路径下,把文件存进去
String singer = req.getParameter("singer");
System.out.println("歌手:"+singer);
// 此时可以查看到 music文件夹里包含了 音乐
// 2、插入数据库
// 看数据库中的信息 title、singer、time、url
String[] titles = fileName.split("\\."); // 毛不易 - 若有缘由.mp3, 用.分割,title=毛不易 - 若有缘由 0下标
String title = titles[0];
System.out.println("title:" + title);
String url = "music\\"+title;
System.out.println("url:"+url);
// 格式化时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String time=sdf.format(new Date());
int userId = user.getId();
MusicDao musicDao = new MusicDao();
int ret = musicDao.insert(title,singer,time,url,userId); // 数据库里存
if (ret == 1){
// 上传成功
resp.sendRedirect("list.html"); // 重定向
}else {
System.out.println("上传失败!");
part.delete(); // 数据库上传失败了,就要把前面服务器上传成功的给删掉
}
}
}
}
7.前端页面设计
采用HTML+CSS+JS设计
不做重点,可寻找模板,稍作改动。
8.最终实现效果
登录:
注册:
列表页:
添加音乐到喜欢列表:
喜欢音乐列表: