preface
-
Time: August 2021 6—8.21
-
Content:
- Some problems about mapping. (mapping 1)
- Niche front end:
- 1 click a book in the list to jump to the details page of the book. (associated table query)
- 2. You can specify an address to enter the registration page. (mapping 2)
- 3. There is a verification code on the registration page.
- 4 md5 encryption. (salt value used)
- 5 book status (want to see / have seen), praise and short comment
-
remarks:
-
Because the contents of these two days are relatively continuous and cross, they are written together (in fact, they were lazy on the 4th and didn't remember much
-
This text is too much, that is, the thinking process is still very important!! (leak finding and vacancy filling player) (it seems to jump, but in fact it has internal coherence, mainly the hole filling process of my own knowledge framework, ha ha, ha ha
-
Some of the notes were added on August 21, but they were brief (it's really delayed
-
In addition, this small project will not be updated for the time being. The code has been posted on GitHub~~~~
-
1. Details page (linked table query)
-
Let's post the code directly here, which is the basic operation of ssm written countless times before. The difficulty is a joint table query.
-
Thinking results:
-
Generally speaking, we can call business at will in the control layer. The classification of the control layer is only for clarity and better understanding. (improve readability)
-
The usual thinking at the beginning of learning is that a dao corresponds to a domain and a service corresponds to a dao, but in practical application ~ the former is no problem. dao now inherits various sql methods of Baidu and doesn't need to write it yourself; The latter is not. For example, a service corresponds to multiple Daos when querying a linked table.
@Service @Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true) public class EvaluationServiceImpl implements EvaluationService { @Autowired EvaluationDao evaluationDao; //To start with members and books, we need to inject these two factors @Autowired BookDao bookDao; @Autowired MemberDao memberDao; @Override public List<Evaluation> selectEvaluationList(Long bookId) { QueryWrapper<Evaluation> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("bookId",bookId); queryWrapper.eq("statu","enable"); queryWrapper.orderByDesc("createTime"); //Find out all the comments on the book List<Evaluation> evList = evaluationDao.selectList(queryWrapper); //There's a book Book book = bookDao.selectById(bookId); for(Evaluation evaluation : evList){ //Members have Member member = memberDao.selectById(evaluation.getMemberId()); evaluation.setBook(book); evaluation.setMember(member); } return evList; } }
-
It seems that the logic is not quite right to add something to the business package with the same name, but it seems that there is nothing wrong to sum up. hhh: the database of the comment table has only user id and no user information. What should we do if the page wants to obtain user information? Send a comment. The name without a comment is always wrong! So what? Generally speaking, it is our joint table query, but this joint table query is a java back-end, unlike the previous sql attack.
-
Too much nonsense. The steps are as follows:
-
domain page, add a property in the category!!!
@TableName public class Evaluation { @TableId(type = IdType.AUTO) @TableField("evaluationId") private Long evaluationId; private Long bookId; private Long memberId; private String content; private Integer score; private Date createTime; private Integer enjoy; private String statu;//Status audit status, enable valid, disable invalid private Date disableTime; private String disableReason; //We need to add some attributes that do not correspond to the table @TableField(exist = false)//It means that there is no corresponding field in the database table and will not participate in the automatic generation of sql private Book book;//This object is checked manually by us @TableField(exist = false) private Member member;
-
-
2-1 mapping 1
Suddenly, the problem of mapping is mainly caused by the mismatch between ssm and so and so view. As mentioned in the previous summary, I will add a summary now~
- There may be a mismatch between dao's: dao needs a corresponding dao with the same name Java one xml, and the mapper space in xml needs to write the corresponding java package path.
- It may be ApplicationContext XML package problem: either the scanning range is too small. It is recommended to scan com Pro, which can scan all @ controllers and @ services; Or it's a lazy cv player like me. When copying and pasting the package, the overly intelligent idea helped me change the path (should I thank it? Find a difference in Wanli code... Find more com in this configuration file, and ctrl+f to find and replace it)
- It is possible that the path in the controller cannot be mapped to the corresponding page: see mapping 2 for details
2-2 mapping 2
-
Thinking process:
- I always thought that the parameters in mapping should be the same as the method name without adding @ ResponseBody. Today, I suddenly found that it can be different!
- @The address in GetMapping is only used for the input of the address bar (and the name to shout when the front end looks for this method). You can write anything.
- The method name can be different from the name of @ GetMapping. Just as a person can have a nickname in addition to his own name, the method can also have his own nickname. The difference is that there can only be one nickname (you can't add two GetMapping? (it doesn't seem to be...? (confused (dog head)
- In addition, GetMapping and PostMapping are suddenly confused. If you enter the address directly in the address bar, can both methods be used? After all, no data is transmitted, so... Do get and post only affect the situation of "transmitting data from the front-end ajax", but not the situation of "calling names directly from the address bar"? (confused)
- And about map, a familiar stranger, I don't quite understand its mechanism. I'll summarize it later when I slowly sort it out!!!!!!!
- I always thought that the parameters in mapping should be the same as the method name without adding @ ResponseBody. Today, I suddenly found that it can be different!
-
Take "enter the specified address in the address bar to jump to the registration page" as an example:
-
Method ①
@GetMapping("/a") public String b(){ return "reg"; }
-
Method ②
@GetMapping("/reg.html") public ModelAndView toReg() { ModelAndView mav = new ModelAndView("reg"); return mav; }
-
Method ③ return page name
ModelMap mm = new ModelMap();
-
Method ④ returns the page name (the return parameter written here should be String, which is not clear. Write notes again, and only distinguish the first two types...)
public ModelAndView displayBookDetail(@PathVariable('bookId')Long bookId,Mode model){ Book book = bookService.getBookId(bookId); model.addAttribute("book",book); }
-
3-1 verification code (display)
pom.xml
- There is only one version in maven!
<!--Verification Code--> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency>
applicationContext.xml
-
Add a bean
<!--to configure kaptcha--> <bean id="defaultKaptcha" class="com.google.code.kaptcha.impl.DefaultKaptcha"> <property name="config"> <bean class="com.google.code.kaptcha.util.Config"> <constructor-arg><!--Constructor--> <props> <!--frame--> <prop key="kaptcha.border">no</prop> <!--Width 120 px--> <prop key="kaptcha.image.width">120</prop> <!--colour--> <prop key="kaptcha.textproducer.font.color">pink</prop> <!--size--> <prop key="kaptcha.textproducer.font.size">40</prop> <!--Several characters--> <prop key="kaptcha.textproducer.char.length">4</prop> </props> </constructor-arg> </bean> </property> </bean>
KaptchaController.java
@Controller public class KaptchaController { @Autowired protected Producer defaultKaptcha; @GetMapping("/verifyCode") public void createVerifyCode(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setDateHeader("Expires",0);//The response expires immediately //Don't cache pictures response.setHeader("Cache-Control", "no-store,no-cache,must-revalidate"); response.setHeader("Cache-Control", "post-check=0,pre-check=0"); response.setHeader("Pragma", "no-cache"); response.setContentType("image/png"); //Generate verification code text String verifyCode = defaultKaptcha.createText(); request.getSession().setAttribute("verifyCode", verifyCode); //Print it out here. During the test, see if it is consistent System.out.println(request.getSession().getAttribute("verifyCode")); //Magic, become a picture BufferedImage image = defaultKaptcha.createImage(verifyCode); //The output stream, from the server to the client, sets up a pipeline to send the picture verification code to your page ServletOutputStream outputStream = response.getOutputStream(); //Write, write the diagram to your client through the pipeline ImageIO.write(image, "png", outputStream); outputStream.flush(); outputStream.close(); //io stream. What are streams and what are they? } }
-
The Producer here is not a class written by itself, but a class in kaptcha
package com.google.code.kaptcha; import java.awt.image.BufferedImage; public interface Producer { BufferedImage createImage(String var1); String createText(); }
3-2 verification code (verification)
-
It's mainly the front-end operation, because there is a long time lag between taking notes and writing code
-
Login authentication
<script> //Click the verification code picture to refresh the verification code $("#imgVerifyCode").click(function () { reloadVerifyCode(); }); //Resend the request and refresh the verification code function reloadVerifyCode(){ //Here, refresh the verification code and set the time difference in the request. $('#imgVerifyCode').attr("src","verifyCode?tp="+new Date().getTime()); } //Action to submit a form $(function () { $('#btnSubmit').click(function () { $.ajax({ url:'/xzw/checkLogin', type:'post', dataType:'json', data:$('#frmLogin').serialize(), success:function (data) { if(data.code=="ok_vc"){ alert(data.msg); //Go to the home page window.location="/xzw/index.html?tp="+new Date().getTime(); }else { //Refresh the page and log in again alert(data.msg); $('#password').val("") $('#nickname').val("") $('#verifyCode').val("") reloadVerifyCode(); } } }); }); }); </script>
-
Validation of registration
<script> //Click the verification code picture to refresh the verification code $("#imgVerifyCode").click(function () { reloadVerifyCode(); }); //Resend the request and refresh the verification code function reloadVerifyCode(){ //Please refresh the verification code here. The time set in the request is wrong. $('#imgVerifyCode').attr("src","verifyCode?tp="+new Date().getTime()); } //Action to submit a form $("#btnSubmit").click(function () { //Form verification var username = $.trim($("#username").val()); var regex = /^.{6,10}$/; if (!regex.test(username)) { alert("Please enter the correct format for user name (6)-10 Bit)"); return; } var password = $.trim($("#password").val()); if (!regex.test(password)) { alert("Please enter the correct password format (6)-10 Bit)"); return; } $btnReg = $(this); $btnReg.text("Processing..."); $btnReg.attr("disabled", "disabled"); //Send ajax request $.ajax({ url: "regist", type: "post", dataType: "json", data: $("#frmLogin").serialize(), success: function (data) { //Result processing: judge the processing status of the server according to the code returned by the server //The server requires that JSON format be returned: //{"code":"0","msg": "processing messages"} console.info("Server response:" , data); if (data.code == "ok_vc") { //The registration success dialog box is displayed alert(data.msg) $('#username').val("") $('#password').val("") $('#nickname').val("") $('#verifyCode').val("") reloadVerifyCode(); } else { //Server verification exception, error message alert(data.msg); //If there is an error, the verification code is regenerated reloadVerifyCode(); } } }); return false; }); </script>
4 md5 encryption
-
What is this encryption technology used for? Let's think about it. If the password we filled in when we registered our account was plainly entered into the database, wouldn't our account password be seen by the database administrator? Therefore, for the mutual trust between users and developers, there is an intermediate link of this encryption: users will encrypt the password when uploading the password, so the user password received by the administrator is a long encrypted string, and when users log in to match the password with the background, they will encrypt the password and match it in the same way.
-
There is a small bug in this place. For example, my password is 123456, and the password of an individual account is 123456. Then the two passwords recorded in the database are the same string...
-
The code is as follows:
package com.pro.util; import org.apache.commons.codec.digest.DigestUtils; /** * @author Yuhua * @since 21.8.5 11:20 */ public class MD5Util { /** * * @param source Ready for encryption * @param salt * @return */ public static String md5Digest(String source,Integer salt){ char[] ca = source.toCharArray(); /*for (int i = 0; i < ca.length; i++) { ca[i] = (char) (ca[i]+salt); }*/ //Character array -- > string String target = new String(ca); String md5 = DigestUtils.md5Hex(target); //abcd-->adfadsfasdfadfsa return md5; } }
-
MemberServiceImpl.java
//register @Override public Member createMember(String username, String password, String nickname){ QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("username",username); List<Member> memberList = memberDao.selectList(queryWrapper); //According to username, if it is found, it indicates that it has been registered if(memberList.size()>0){ //Unable to register throw new ServiceException("801","The user name has been registered!"); } // Member member = new Member(); member.setUsername(username); member.setNickName(nickname); //Password domain int salt = new Random().nextInt(1000) + 1000; String md5 = MD5Util.md5Digest(password,salt); member.setPassword(md5); member.setSalt(salt); member.setCreateTime(new Date()); System.out.println(member.toString()); //add to memberDao.insert(member); return member; }
//Log in and check whether the user and password are correct @Override public Member checkLogin(String username, String password){ QueryWrapper<Member> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username",username); Member member = memberDao.selectOne(queryWrapper); if(member==null){ throw new ServiceException("802","user does not exist"); } //You can stop here and explain that there is this user in the table String md5 = MD5Util.md5Digest(password, member.getSalt()); if(!md5.equalsIgnoreCase(member.getPassword())){ throw new ServiceException("803", "Incorrect password entered"); } //If the code can go here, the description is correct return member; }
5 book status, likes and comments
-
This part goes directly to the code.. Incomplete, go directly to GitHub to get Kangkang... The address is posted at the beginning of the article...
<script> $.fn.raty.defaults.path = '/xzw/resources/raty/lib/images'; $(function () { $(".stars").raty({readOnly: true}); }) $(function () { //Note, note <!-- If the status is not empty, highlight is set, and the element corresponding to the found reading status is added as highlighted?? These two question marks mean No null--> <#if memberReadState ??>/* If the status exists*/ $('[data-read-state="${memberReadState.readState}"]').addClass('highlight'); </#if> //If you don't log in, you can't read the status, write comments and like <#if !loginMember ??> $('[data-read-state],#btnEvaluation,[data-evaluation-id]').click(function () { alert("Please login before operation!") }) </#if> <#if loginMember ??> $('[data-read-state]').click(function () { var readState = $(this).data('read-state'); //post is four parameters $.post('/xzw/updateReadState',{ "memberId":${loginMember.memberId}, "bookId":${book.bookId}, "readState":readState },function () { if(data.code=="ok_mrs"){ $('[data-read-state]').removeClass('highlight');//Clear highlight $('[data-read-state="'+readState+'"]').addClass('highlight'); } },'json'); window.location.reload(true); }); //Click the Comment button $("#btnEvaluation").click(function () { $('#score').raty({});// Convert span to star assembly //Show write essay div $('#dlgEvaluation').modal('show'); }) //Submit comments $("#btnSubmit").click(function () { var score = $('#score').raty("score"); var content = $('#content').val(); //No content, no submission if(score==0||$.trim(content)==""){ return; } $.post("/xzw/evaluate",{ "score":score, "bookId": ${book.bookId}, "memberId":${loginMember.memberId}, "content":content },function (data) { if(data.code=="ok_ev"){ //Reload the current page to get the latest comments window.location.reload(); } },'json') }); $('[data-evaluation-id]').click(function(){ var evaluationId = $(this).data("evaluation-id"); $.post('/xzw/enjoy',{"evaluationId":evaluationId},function (data) { if(data.code=="ok_en"){ window.location.reload();//This sentence seems useless?!! $('[data-evaluation-id="'+evaluationId+'"] span').text(data.evaluation.enjoy) } },'json'); }); </#if> }); </script>
Something at the front
display:none corresponds to jQuery's show