Joining tables in SQLite

本文介绍了如何在SQLite中使用JOIN语句来连接多个表。通过实际的例子,展示了INNER JOIN、NATURAL INNER JOIN、CROSS INNER JOIN及LEFT OUTER JOIN的用法,并提供了具体的查询结果。

Joining tables

In this part of the SQLite tutorial, we will join tables in SQLite.

The real power and benefits from relational databases come from joining tables. The SQL JOIN clause combines records from two or more tables in a database. There are basically two types of joins. INNER and OUTER.

In this part of the tutorial, we will work with Customers and Reservations tables.

sqlite> SELECT * FROM Customers;
CustomerId  Name       
----------  -----------
1           Paul Novak 
2           Terry Neils
3           Jack Fonda 
4           Tom Willis 

Values from the Customers table.

sqlite> SELECT * FROM Reservations;
Id  CustomerId  Day       
--  ----------  ----------
1   1           2009-22-11
2   2           2009-28-11
3   2           2009-29-11
4   1           2009-29-11
5   3           2009-02-12

Values from the Reservations tables.

Inner joins

The inner join is the most common type of joins. It is the default join also. The inner join selects only those records from database tables that have matching values. We have three types of INNER JOINS. INNER JOIN, NATURAL INNER JOIN and CROSS INNER JOIN. The INNER keyword can be omitted.

INNER JOIN

sqlite> SELECT Name, Day FROM Customers AS C JOIN Reservations
   ...> AS R ON C.CustomerId=R.CustomerId;
Name         Day        
-----------  -----------
Paul Novak   2009-22-11 
Terry Neils  2009-28-11 
Terry Neils  2009-29-11 
Paul Novak   2009-29-11 
Jack Fonda   2009-02-12 

In this SELECT statement, we have selected all customers, that have made some reservations. Note, that we have omitted the INNER keyword.

The statement is equivalent to the following one:

sqlite> SELECT Name, Day FROM Customers, Reservations
   ...> WHERE Customers.CustomerId = Reservations.CustomerId;
Name        Day        
----------  -----------
Paul Novak  2009-22-11 
Terry Neil  2009-28-11 
Terry Neil  2009-29-11 
Paul Novak  2009-29-11 
Jack Fonda  2009-02-12

We get the same data.

NATURAL INNER JOIN

The NATURAL INNER JOIN automatically uses all the matching column names for the join. In our tables, we have a column named CustomerId in both tables.

sqlite> SELECT Name, Day FROM Customers NATURAL JOIN Reservations;
Name         Day       
-----------  ----------
Paul Novak   2009-22-11
Terry Neils  2009-28-11
Terry Neils  2009-29-11
Paul Novak   2009-29-11
Jack Fonda   2009-02-12

We get the same data. The SQL statement is less verbose.

CROSS INNER JOIN

The CROSS INNER JOIN combines all records from one table with all records from another table. This type of join has little practical value. It is also called a cartesian product of records.

sqlite> SELECT Name, Day FROM Customers CROSS JOIN Reservations;
Name         Day       
-----------  ----------
Paul Novak   2009-22-11
Paul Novak   2009-28-11
Paul Novak   2009-29-11
Paul Novak   2009-29-11
Paul Novak   2009-02-12
Terry Neils  2009-22-11
Terry Neils  2009-28-11
Terry Neils  2009-29-11
Terry Neils  2009-29-11
Terry Neils  2009-02-12
...

The same result can be achieved with the following SQL statement:

sqlite> SELECT Name, Day FROM Customers, Reservations;

Outer joins

An outer join does not require each record in the two joined tables to have a matching record. There are three types of outer joins. Left outer joins, right outer joins, and full outer joins. SQLite only supports left outer joins.

LEFT OUTER JOIN

The LEFT OUTER JOIN returns all values from the left table, even if there is no match with the right table. It such rows, there will be NULL values. In other words, left outer join returns all the values from the left table, plus matched values from the right table. Note, that the OUTER keyword can be omitted.

sqlite> SELECT Name, Day FROM Customers LEFT JOIN Reservations
   ...> ON Customers.CustomerId = Reservations.CustomerId;
Name         Day        
-----------  -----------
Paul Novak   2009-22-11 
Paul Novak   2009-29-11 
Terry Neils  2009-28-11 
Terry Neils  2009-29-11 
Jack Fonda   2009-02-12 
Tom Willis   NULL  

Here we have all customers with their reservations, plus a customer, who has no reservation. There is NULL value in his row.

We can use the USING keyword to achieve the same result. The SQL statement will be less verbose.

sqlite> SELECT Name, Day FROM Customers LEFT JOIN Reservations
   ...> USING (CustomerId);
Name         Day        
-----------  -----------
Paul Novak   2009-22-11 
Paul Novak   2009-29-11 
Terry Neils  2009-28-11 
Terry Neils  2009-29-11 
Jack Fonda   2009-02-12 
Tom Willis   NULL

Same result, with shorter SQL statement.

NATURAL LEFT OUTER JOIN

The NATURAL LEFT OUTER JOIN automatically uses all the matching column names for the join.

sqlite> SELECT Name, Day FROM Customers NATURAL LEFT OUTER JOIN Reservations;
Name         Day       
-----------  ----------
Paul Novak   2009-22-11
Paul Novak   2009-29-11
Terry Neils  2009-28-11
Terry Neils  2009-29-11
Jack Fonda   2009-02-12
Tom Willis   NULL  

Same result, but with fewer key strokes.

In this part of the SQLite tutorial, we were joining tables.


原文:http://zetcode.com/db/sqlite/joins/


/************************************************************************************************** Filename: ZMain.c Revised: $Date: 2009-09-17 20:35:33 -0700 (Thu, 17 Sep 2009) $ Revision: $Revision: 20782 $ Description: Startup and shutdown code for ZStack Notes: This version targets the Chipcon CC2530 Copyright 2005-2009 Texas Instruments Incorporated. All rights reserved. IMPORTANT: Your use of this Software is limited to those specific rights granted under the terms of a software license agreement between the user who downloaded the software, his/her employer (which must be your employer) and Texas Instruments Incorporated (the "License"). You may not use this Software unless you agree to abide by the terms of the License. The License limits your use, and you acknowledge, that the Software may not be modified, copied or distributed unless embedded on a Texas Instruments microcontroller or used solely and exclusively in conjunction with a Texas Instruments radio frequency transceiver, which is integrated into your product. Other than for the foregoing purpose, you may not use, reproduce, copy, prepare derivative works of, modify, distribute, perform, display or sell this Software and/or its documentation for any purpose. YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED 揂S IS?WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. Should you have any questions regarding your right to use this Software, contact Texas Instruments Incorporated at www.TI.com. **************************************************************************************************/ /********************************************************************* * INCLUDES */ #include "ZComDef.h" #include "OSAL.h" #include "OSAL_Nv.h" #include "OnBoard.h" #include "ZMAC.h" #ifndef NONWK #include "AF.h" #endif /* Hal */ #include "hal_lcd.h" #include "hal_led.h" #include "hal_adc.h" #include "hal_drivers.h" #include "hal_assert.h" #include "hal_flash.h" /********************************************************************* * MACROS */ /********************************************************************* * CONSTANTS */ // Maximun number of Vdd samples checked before go on #define MAX_VDD_SAMPLES 3 #define ZMAIN_VDD_LIMIT HAL_ADC_VDD_LIMIT_4 /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */ extern bool HalAdcCheckVdd (uint8 limit); /********************************************************************* * LOCAL VARIABLES */ /********************************************************************* * LOCAL FUNCTIONS */ static void zmain_dev_info( void ); static void zmain_ext_addr( void ); static void zmain_vdd_check( void ); #ifdef LCD_SUPPORTED static void zmain_lcd_init( void ); #endif /********************************************************************* * @fn main * @brief First function called after startup. * @return don't care */ int main( void ) { // Turn off interrupts osal_int_disable( INTS_ALL ); //关闭所有中断 // Initialization for board related stuff such as LEDs HAL_BOARD_INIT(); //初始化系统时钟 // Make sure supply voltage is high enough to run zmain_vdd_check(); //检查芯片电压是否正常 // Initialize board I/O InitBoard( OB_COLD ); //初始化I/O ,LED 、Timer 等 // Initialze HAL drivers HalDriverInit(); //初始化芯片各硬件模块 // Initialize NV System osal_nv_init( NULL ); //初始化Flash 存储器 // Initialize the MAC ZMacInit(); //初始化MAC 层 // Determine the extended address zmain_ext_addr(); //确定IEEE 64位地址 // Initialize basic NV items zgInit(); //初始化非易失变量 #ifndef NONWK // Since the AF isn't a task, call it's initialization routine afInit(); #endif // Initialize the operating system osal_init_system(); //初始化操作系统 // Allow interrupts osal_int_enable( INTS_ALL ); //使能全部中断 // Final board initialization InitBoard( OB_READY ); //最终板载初始化 // Display information about this device zmain_dev_info(); //显示设备信息 /* Display the device info on the LCD */ #ifdef LCD_SUPPORTED zmain_lcd_init(); //初始化LCD #endif #ifdef WDT_IN_PM1 /* If WDT is used, this is a good place to enable it. */ WatchDogEnable( WDTIMX ); #endif osal_start_system(); // No Return from here 执行操作系统,进去后不会返回 return 0; // Shouldn't get here. } // main() /********************************************************************* * @fn zmain_vdd_check * @brief Check if the Vdd is OK to run the processor. * @return Return if Vdd is ok; otherwise, flash LED, then reset *********************************************************************/ static void zmain_vdd_check( void ) { uint8 vdd_passed_count = 0; bool toggle = 0; // Repeat getting the sample until number of failures or successes hits MAX // then based on the count value, determine if the device is ready or not while ( vdd_passed_count < MAX_VDD_SAMPLES ) { if ( HalAdcCheckVdd (ZMAIN_VDD_LIMIT) ) { vdd_passed_count++; // Keep track # times Vdd passes in a row MicroWait (10000); // Wait 10ms to try again } else { vdd_passed_count = 0; // Reset passed counter MicroWait (50000); // Wait 50ms MicroWait (50000); // Wait another 50ms to try again } /* toggle LED1 and LED2 */ if (vdd_passed_count == 0) { if ((toggle = !(toggle))) HAL_TOGGLE_LED1(); else HAL_TOGGLE_LED2(); } } /* turn off LED1 */ HAL_TURN_OFF_LED1(); HAL_TURN_OFF_LED2(); } /************************************************************************************************** * @fn zmain_ext_addr * * @brief Execute a prioritized search for a valid extended address and write the results * into the OSAL NV system for use by the system. Temporary address not saved to NV. * * input parameters * * None. * * output parameters * * None. * * @return None. ************************************************************************************************** */ static void zmain_ext_addr(void) { uint8 nullAddr[Z_EXTADDR_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; uint8 writeNV = TRUE; // First check whether a non-erased extended address exists in the OSAL NV. if ((SUCCESS != osal_nv_item_init(ZCD_NV_EXTADDR, Z_EXTADDR_LEN, NULL)) || (SUCCESS != osal_nv_read(ZCD_NV_EXTADDR, 0, Z_EXTADDR_LEN, aExtendedAddress)) || (osal_memcmp(aExtendedAddress, nullAddr, Z_EXTADDR_LEN))) { // Attempt to read the extended address from the location on the lock bits page // where the programming tools know to reserve it. HalFlashRead(HAL_FLASH_IEEE_PAGE, HAL_FLASH_IEEE_OSET, aExtendedAddress, Z_EXTADDR_LEN); if (osal_memcmp(aExtendedAddress, nullAddr, Z_EXTADDR_LEN)) { // Attempt to read the extended address from the designated location in the Info Page. if (!osal_memcmp((uint8 *)(P_INFOPAGE+HAL_INFOP_IEEE_OSET), nullAddr, Z_EXTADDR_LEN)) { osal_memcpy(aExtendedAddress, (uint8 *)(P_INFOPAGE+HAL_INFOP_IEEE_OSET), Z_EXTADDR_LEN); } else // No valid extended address was found. { uint8 idx; #if !defined ( NV_RESTORE ) writeNV = FALSE; // Make this a temporary IEEE address #endif /* Attempt to create a sufficiently random extended address for expediency. * Note: this is only valid/legal in a test environment and * must never be used for a commercial product. */ for (idx = 0; idx < (Z_EXTADDR_LEN - 2);) { uint16 randy = osal_rand(); aExtendedAddress[idx++] = LO_UINT16(randy); aExtendedAddress[idx++] = HI_UINT16(randy); } // Next-to-MSB identifies ZigBee devicetype. #if ZG_BUILD_COORDINATOR_TYPE && !ZG_BUILD_JOINING_TYPE aExtendedAddress[idx++] = 0x10; #elif ZG_BUILD_RTRONLY_TYPE aExtendedAddress[idx++] = 0x20; #else aExtendedAddress[idx++] = 0x30; #endif // MSB has historical signficance. aExtendedAddress[idx] = 0xF8; } } if (writeNV) { (void)osal_nv_write(ZCD_NV_EXTADDR, 0, Z_EXTADDR_LEN, aExtendedAddress); } } // Set the MAC PIB extended address according to results from above. (void)ZMacSetReq(MAC_EXTENDED_ADDRESS, aExtendedAddress); } /************************************************************************************************** * @fn zmain_dev_info * * @brief This displays the IEEE (MSB to LSB) on the LCD. * * input parameters * * None. * * output parameters * * None. * * @return None. ************************************************************************************************** */ static void zmain_dev_info(void) { #ifdef LCD_SUPPORTED uint8 i; uint8 *xad; uint8 lcd_buf[Z_EXTADDR_LEN*2+1]; // Display the extended address. xad = aExtendedAddress + Z_EXTADDR_LEN - 1; for (i = 0; i < Z_EXTADDR_LEN*2; xad--) { uint8 ch; ch = (*xad >> 4) & 0x0F; lcd_buf[i++] = ch + (( ch < 10 ) ? '0' : '7'); ch = *xad & 0x0F; lcd_buf[i++] = ch + (( ch < 10 ) ? '0' : '7'); } lcd_buf[Z_EXTADDR_LEN*2] = '\0'; HalLcdWriteString( "IEEE: ", HAL_LCD_LINE_1 ); HalLcdWriteString( (char*)lcd_buf, HAL_LCD_LINE_2 ); #endif } #ifdef LCD_SUPPORTED /********************************************************************* * @fn zmain_lcd_init * @brief Initialize LCD at start up. * @return none *********************************************************************/ static void zmain_lcd_init ( void ) { #ifdef SERIAL_DEBUG_SUPPORTED { HalLcdWriteString( "TexasInstruments", HAL_LCD_LINE_1 ); #if defined( MT_MAC_FUNC ) #if defined( ZDO_COORDINATOR ) HalLcdWriteString( "MAC-MT Coord", HAL_LCD_LINE_2 ); #else HalLcdWriteString( "MAC-MT Device", HAL_LCD_LINE_2 ); #endif // ZDO #elif defined( MT_NWK_FUNC ) #if defined( ZDO_COORDINATOR ) HalLcdWriteString( "NWK Coordinator", HAL_LCD_LINE_2 ); #else HalLcdWriteString( "NWK Device", HAL_LCD_LINE_2 ); #endif // ZDO #endif // MT_FUNC } #endif // SERIAL_DEBUG_SUPPORTED } #endif /********************************************************************* *********************************************************************/ 根据这个代码 写一个pyqt的UI界面,实时接收终端的数据,之后要 存储在数据库里
09-05
package com.server; import java.sql.*; import java.security.SecureRandom; import java.util.Base64; import org.apache.commons.codec.digest.Crypt; public class MessageDatabase_217230206 { private static MessageDatabase_217230206 dbInstance = null; private Connection dbconnection = null; private SecureRandom secureRandom = new SecureRandom(); public static synchronized MessageDatabase_217230206 getInstance() { if (dbInstance == null) { dbInstance = new MessageDatabase_217230206(); } return dbInstance; } public void open(String databaseName) throws SQLException { if (dbconnection != null && !dbconnection.isClosed()) { return; } String connectionString = "jdbc:sqlite:" + databaseName; dbconnection = DriverManager.getConnection(connectionString); System.out.println("Connected to database: " + databaseName); initializeTables(); } public void close() throws SQLException { if (dbconnection != null && !dbconnection.isClosed()) { dbconnection.close(); System.out.println("Database connection closed"); } } private void initializeTables() throws SQLException { String createUsersTable = "CREATE TABLE IF NOT EXISTS users (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "username VARCHAR(50) UNIQUE NOT NULL, " + "password VARCHAR(255) NOT NULL, " + "email VARCHAR(50) NOT NULL, " + "userNickname VARCHAR(50) NOT NULL, " + "salt VARCHAR(100) NOT NULL" + ");"; String createMessagesTable = "CREATE TABLE IF NOT EXISTS messages (" + "locationID INTEGER PRIMARY KEY AUTOINCREMENT, " + "locationName VARCHAR(100) NOT NULL, " + "locationDescription TEXT NOT NULL, " + "locationCity VARCHAR(50) NOT NULL, " + "locationCountry VARCHAR(50), " + "locationStreetAddress VARCHAR(100), " + "latitude REAL, " + "longitude REAL, " + "originalPostingTime BIGINT NOT NULL, " + "originalPoster VARCHAR(50) NOT NULL, " + "username VARCHAR(50) NOT NULL, " + "weather VARCHAR(50)" + ");"; try (Statement createStatement = dbconnection.createStatement()) { createStatement.executeUpdate(createUsersTable); createStatement.executeUpdate(createMessagesTable); System.out.println("Database tables created successfully"); } } public boolean userExists(String username) throws SQLException { final String sql = "SELECT COUNT(*) as count FROM users WHERE username = ?"; try (PreparedStatement pstmt = dbconnection.prepareStatement(sql)) { pstmt.setString(1, username); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return rs.getInt("count") > 0; } return false; } } private String hashPassword(String password) { byte[] bytes = new byte[13]; secureRandom.nextBytes(bytes); String saltBytes = Base64.getEncoder().encodeToString(bytes); String salt = "$6$" + saltBytes; return Crypt.crypt(password, salt); } public boolean addUser(String username, String password, String email, String userNickname) throws SQLException { final String sql = "INSERT INTO users (username, password, email, userNickname, salt) VALUES (?, ?, ?, ?, ?)"; try (PreparedStatement pstmt = dbconnection.prepareStatement(sql)) { String hashedPassword = hashPassword(password); pstmt.setString(1, username); pstmt.setString(2, hashedPassword); pstmt.setString(3, email); pstmt.setString(4, userNickname); pstmt.setString(5, hashedPassword.substring(0, 20)); pstmt.executeUpdate(); return true; } catch (SQLException e) { if (e.getErrorCode() == 19 || e.getMessage().contains("UNIQUE constraint failed")) { return false; } throw e; } } public boolean validateUser(String username, String password) throws SQLException { final String sql = "SELECT password FROM users WHERE username = ?"; try (PreparedStatement pstmt = dbconnection.prepareStatement(sql)) { pstmt.setString(1, username); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { String storedHashedPassword = rs.getString("password"); String computedHash = Crypt.crypt(password, storedHashedPassword); return storedHashedPassword.equals(computedHash); } return false; } } public String getUserNickname(String username) throws SQLException { final String sql = "SELECT userNickname FROM users WHERE username = ?"; try (PreparedStatement pstmt = dbconnection.prepareStatement(sql)) { pstmt.setString(1, username); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return rs.getString("userNickname"); } return null; } } public boolean addMessage(String locationName, String locationDescription, String locationCity, String locationCountry, String locationStreetAddress, Double latitude, Double longitude, long originalPostingTime, String username, String weather) throws SQLException { if (!userExists(username)) { System.out.println("User " + username + " does not exist"); return false; } String userNickname = getUserNickname(username); if (userNickname == null) { userNickname = username; } final String sql = "INSERT INTO messages (locationName, locationDescription, locationCity, " + "locationCountry, locationStreetAddress, latitude, longitude, " + "originalPostingTime, originalPoster, username, weather) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; try (PreparedStatement pstmt = dbconnection.prepareStatement(sql)) { pstmt.setString(1, locationName); pstmt.setString(2, locationDescription); pstmt.setString(3, locationCity); pstmt.setString(4, locationCountry); pstmt.setString(5, locationStreetAddress); if (latitude != null) { pstmt.setDouble(6, latitude); } else { pstmt.setNull(6, Types.DOUBLE); } if (longitude != null) { pstmt.setDouble(7, longitude); } else { pstmt.setNull(7, Types.DOUBLE); } pstmt.setLong(8, originalPostingTime); pstmt.setString(9, userNickname); pstmt.setString(10, username); if (weather != null) { pstmt.setString(11, weather); } else { pstmt.setNull(11, Types.VARCHAR); } pstmt.executeUpdate(); return true; } catch (SQLException e) { System.err.println("Error adding message: " + e.getMessage()); e.printStackTrace(); return false; } } public ResultSet getAllMessages() throws SQLException { final String sql = "SELECT locationID, locationName, locationDescription, locationCity, " + "locationCountry, locationStreetAddress, latitude, longitude, " + "originalPostingTime, originalPoster, weather " + "FROM messages " + "ORDER BY originalPostingTime DESC"; Statement statement = dbconnection.createStatement(); return statement.executeQuery(sql); } }package com.server; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpExchange; import java.io.*; import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.stream.Collectors; import org.json.JSONException; import org.json.JSONObject; public class RegistrationHandler_217230206 implements HttpHandler{ private final UserAuthenticator_217230206 userAuthenticator; private final MessageDatabase_217230206 database; public RegistrationHandler_217230206(UserAuthenticator_217230206 userAuthenticator, MessageDatabase_217230206 database) { this.userAuthenticator = userAuthenticator; this.database = database; } @Override public void handle(HttpExchange exchange) throws IOException { String requestMethod = exchange.getRequestMethod(); try { if (requestMethod.equalsIgnoreCase("POST")) { handlePostRequest(exchange); } else { handleUnsupportedRequest(exchange); } } catch (Exception e) { sendErrorResponse(exchange, 500, "Internal Server Error"); } } private void handlePostRequest(HttpExchange exchange) throws IOException { String contentType = exchange.getRequestHeaders().getFirst("Content-Type"); if (contentType == null || !contentType.equals("application/json")) { sendErrorResponse(exchange, 400, "Content-Type must be application/json"); return; } String content; try (InputStream stream = exchange.getRequestBody(); InputStreamReader isr = new InputStreamReader(stream, StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(isr)) { content = br.lines().collect(Collectors.joining("\n")); } try { JSONObject json = new JSONObject(content); if (!json.has("username") || !json.has("password") || !json.has("email") || !json.has("userNickname")) { sendErrorResponse(exchange, 400, "Missing required fields: username, password, email, userNickname"); return; } String username = json.getString("username").trim(); String password = json.getString("password").trim(); String email = json.getString("email").trim(); String userNickname = json.getString("userNickname").trim(); if (username.isEmpty() || password.isEmpty() || email.isEmpty() || userNickname.isEmpty()) { sendErrorResponse(exchange, 400, "All fields must not be empty"); return; } boolean dbSuccess = database.addUser(username, password, email, userNickname); if (dbSuccess) { userAuthenticator.addUser(username, password, email, userNickname); JSONObject responseJson = new JSONObject(); responseJson.put("message", "User registered successfully"); responseJson.put("username", username); sendJsonResponse(exchange, 200, responseJson); } else { sendErrorResponse(exchange, 409, "User already registered"); } } catch (JSONException e) { sendErrorResponse(exchange, 400, "Invalid JSON format: " + e.getMessage()); } catch (SQLException e) { if (e.getMessage() != null && (e.getMessage().contains("UNIQUE constraint failed") || e.getMessage().contains("unique constraint") || e.getErrorCode() == 19)) { sendErrorResponse(exchange, 409, "User already registered"); } else { System.err.println("Database error during registration: " + e.getMessage()); sendErrorResponse(exchange, 500, "Database error: " + e.getMessage()); } } catch (Exception e) { System.err.println("Unexpected error during registration: " + e.getMessage()); sendErrorResponse(exchange, 500, "Internal server error"); } } private void handleUnsupportedRequest(HttpExchange exchange) throws IOException { sendErrorResponse(exchange, 400, "Not supported"); } private void sendErrorResponse(HttpExchange exchange, int code, String message) throws IOException { JSONObject errorJson = new JSONObject(); errorJson.put("error", message); sendJsonResponse(exchange, code, errorJson); } private void sendJsonResponse(HttpExchange exchange, int code, JSONObject json) throws IOException { String response = json.toString(); byte[] bytes = response.getBytes(StandardCharsets.UTF_8); exchange.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8"); exchange.sendResponseHeaders(code, bytes.length); try (OutputStream outputStream = exchange.getResponseBody()) { outputStream.write(bytes); } } } package com.server; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import org.json.JSONObject; import org.json.JSONArray; import org.json.JSONException; import java.io.*; import java.nio.charset.StandardCharsets; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Base64; public class RequestHandler_217230206 implements HttpHandler { private final MessageDatabase_217230206 database; private final WeatherServiceClient_217230206 weatherClient; public RequestHandler_217230206(MessageDatabase_217230206 database) { this.database = database; this.weatherClient = new WeatherServiceClient_217230206("http://localhost:4001/weather"); } @Override public void handle(HttpExchange exchange) throws IOException { String requestMethod = exchange.getRequestMethod(); try { if (requestMethod.equalsIgnoreCase("GET")) { handleGetRequest(exchange); } else if (requestMethod.equalsIgnoreCase("POST")) { handlePostRequest(exchange); } else { sendErrorResponse(exchange, 405, "Method Not Allowed"); } } catch (Exception e) { sendErrorResponse(exchange, 500, "Internal Server Error"); } } private void handleGetRequest(HttpExchange exchange) throws IOException { if (!authenticateUser(exchange)) { return; } try { ResultSet rs = database.getAllMessages(); JSONArray responseArray = new JSONArray(); boolean hasMessages = false; while (rs.next()) { hasMessages = true; JSONObject messageJson = new JSONObject(); messageJson.put("locationID", rs.getInt("locationID")); messageJson.put("locationName", rs.getString("locationName")); messageJson.put("locationCountry", rs.getString("locationCountry")); messageJson.put("locationDescription", rs.getString("locationDescription")); messageJson.put("locationCity", rs.getString("locationCity")); messageJson.put("longitude", rs.getDouble("longitude")); messageJson.put("latitude", rs.getDouble("latitude")); String locationCountry = rs.getString("locationCountry"); if (locationCountry != null && !rs.wasNull()) { messageJson.put("locationCountry", locationCountry); } else { messageJson.put("locationCountry", ""); } String locationStreetAddress = rs.getString("locationStreetAddress"); if (locationStreetAddress != null && !rs.wasNull()) { messageJson.put("locationStreetAddress", locationStreetAddress); } else { messageJson.put("locationStreetAddress", ""); } double latitude = rs.getDouble("latitude"); if (!rs.wasNull()) { messageJson.put("latitude", latitude); } double longitude = rs.getDouble("longitude"); if (!rs.wasNull()) { messageJson.put("longitude", longitude); } messageJson.put("originalPoster", rs.getString("originalPoster")); long epochMillis = rs.getLong("originalPostingTime"); String isoTime = convertEpochToISO(epochMillis); messageJson.put("originalPostingTime", isoTime); String weather = rs.getString("weather"); if (!rs.wasNull() && weather != null) { messageJson.put("weather", weather); } responseArray.put(messageJson); } try { if (rs != null) { Statement stmt = rs.getStatement(); rs.close(); if (stmt != null) { stmt.close(); } } } catch (SQLException e) { System.err.println("Error closing resources: " + e.getMessage()); } if (!hasMessages) { exchange.sendResponseHeaders(204, -1); } else { sendJsonResponse(exchange, 200, responseArray); } } catch (SQLException e) { sendErrorResponse(exchange, 500, "Database error: " + e.getMessage()); } } private void handlePostRequest(HttpExchange exchange) throws IOException { if (!authenticateUser(exchange)) { return; } String contentType = exchange.getRequestHeaders().getFirst("Content-Type"); if (contentType == null || !contentType.equals("application/json")) { sendErrorResponse(exchange, 400, "Content-Type must be application/json"); return; } String requestBody = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8); try { JSONObject json = new JSONObject(requestBody); if (!json.has("locationName") || !json.has("locationDescription") || !json.has("locationCity") || !json.has("originalPostingTime")) { sendErrorResponse(exchange, 400, "Missing required fields: locationName, locationDescription, locationCity, originalPostingTime"); return; } String locationName = json.getString("locationName").trim(); String locationDescription = json.getString("locationDescription").trim(); String locationCity = json.getString("locationCity").trim(); String originalPostingTimeStr = json.getString("originalPostingTime").trim(); String locationCountry = json.optString("locationCountry", "").trim(); String locationStreetAddress = json.optString("locationStreetAddress", "").trim(); Double latitude = null; Double longitude = null; if (json.has("latitude") && !json.isNull("latitude")) { try { latitude = json.getDouble("latitude"); } catch (Exception e) { } } if (json.has("longitude") && !json.isNull("longitude")) { try { longitude = json.getDouble("longitude"); } catch (Exception e) { } } String weather = null; boolean hasWeatherInRequest = json.has("weather") && !json.isNull("weather"); if (hasWeatherInRequest) { weather = json.optString("weather", null); } if (locationName.isEmpty() || locationDescription.isEmpty() || locationCity.isEmpty()) { sendErrorResponse(exchange, 400, "All required fields must not be empty"); return; } long epochMillis = validateAndParseTimestamp(exchange, originalPostingTimeStr); if (epochMillis == -1) { return; } String authHeader = exchange.getRequestHeaders().getFirst("Authorization"); String username = extractUsernameFromAuth(authHeader); if (latitude != null && longitude != null) { String weatherInfo = weatherClient.getWeather(latitude, longitude); if (weatherInfo != null) { weather = weatherInfo; System.out.println("Retrieved weather: " + weatherInfo); } else { System.out.println("Failed to retrieve weather information"); } } boolean success = database.addMessage(locationName, locationDescription, locationCity, locationCountry, locationStreetAddress, latitude, longitude, epochMillis, username, weather); if (success) { JSONObject responseJson = new JSONObject(); responseJson.put("message", "Message stored successfully"); responseJson.put("locationName", locationName); responseJson.put("originalPostingTime", originalPostingTimeStr); if (latitude != null) { responseJson.put("latitude", latitude); } if (longitude != null) { responseJson.put("longitude", longitude); } if (weather != null) { responseJson.put("weather", weather); } sendJsonResponse(exchange, 200, responseJson); } else { sendErrorResponse(exchange, 500, "Failed to store message"); } } catch (JSONException e) { sendErrorResponse(exchange, 400, "Invalid JSON format: " + e.getMessage()); } catch (SQLException e) { sendErrorResponse(exchange, 500, "Database error: " + e.getMessage()); } } private boolean authenticateUser(HttpExchange exchange) throws IOException { String authHeader = exchange.getRequestHeaders().getFirst("Authorization"); if (authHeader == null || !authHeader.startsWith("Basic ")) { sendErrorResponse(exchange, 401, "Authentication required"); return false; } try { String encodedCredentials = authHeader.substring("Basic ".length()).trim(); byte[] decodedBytes = Base64.getDecoder().decode(encodedCredentials); String credentials = new String(decodedBytes, StandardCharsets.UTF_8); String[] parts = credentials.split(":", 2); if (parts.length != 2) { sendErrorResponse(exchange, 401, "Invalid authentication credentials"); return false; } String userName = parts[0]; String password = parts[1]; if (database.validateUser(userName, password)) { return true; } else { sendErrorResponse(exchange, 401, "Invalid username or password"); return false; } } catch (Exception e) { sendErrorResponse(exchange, 401, "Invalid authentication credentials"); return false; } } private String extractUsernameFromAuth(String authHeader) { try { String encodedCredentials = authHeader.substring("Basic ".length()).trim(); byte[] decodedBytes = Base64.getDecoder().decode(encodedCredentials); String credentials = new String(decodedBytes, StandardCharsets.UTF_8); String[] parts = credentials.split(":", 2); if (parts.length == 2) { return parts[0]; } } catch (Exception e) { } return null; } private long validateAndParseTimestamp(HttpExchange exchange, String timestampStr) throws IOException { if (timestampStr == null || timestampStr.trim().isEmpty()) { sendErrorResponse(exchange, 400, "Timestamp cannot be empty"); return -1; } try { Instant instant = Instant.parse(timestampStr); long epochMillis = instant.toEpochMilli(); if (!isValidTimestamp(epochMillis)) { sendErrorResponse(exchange, 400, "Invalid timestamp value"); return -1; } return epochMillis; } catch (DateTimeParseException e) { sendErrorResponse(exchange, 400, "Invalid timestamp format. Use ISO 8601 UTC format like: 2024-01-15T10:30:00.000Z"); return -1; } } private boolean isValidTimestamp(long epochMillis) { long currentTime = System.currentTimeMillis(); long reasonablePast = currentTime - (100L * 365 * 24 * 60 * 60 * 1000); long reasonableFuture = currentTime + (10L * 365 * 24 * 60 * 60 * 1000); return epochMillis >= reasonablePast && epochMillis <= reasonableFuture; } private String convertEpochToISO(long epochMillis) { return ZonedDateTime.ofInstant( java.time.Instant.ofEpochMilli(epochMillis), ZoneId.of("UTC") ).format(DateTimeFormatter.ISO_INSTANT); } private void sendJsonResponse(HttpExchange exchange, int statusCode, Object json) throws IOException { String response = json.toString(); byte[] responseBytes = response.getBytes(StandardCharsets.UTF_8); exchange.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8"); exchange.sendResponseHeaders(statusCode, responseBytes.length); try (OutputStream os = exchange.getResponseBody()) { os.write(responseBytes); } } private void sendErrorResponse(HttpExchange exchange, int code, String message) throws IOException { JSONObject errorJson = new JSONObject(); errorJson.put("error", message); sendJsonResponse(exchange, code, errorJson); } }package com.server; import com.sun.net.httpserver.HttpsServer; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsParameters; import com.sun.net.httpserver.HttpContext; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import java.io.*; import java.net.InetSocketAddress; import java.security.KeyStore; import java.sql.SQLException; import javax.net.ssl.KeyManagerFactory; import java.util.concurrent.Executors; public class Server { private static SSLContext myServerSSLContext(String keystorePath, String keystorePassword) throws Exception { KeyStore ks = KeyStore.getInstance("JKS"); File keystoreFile = new File(keystorePath); if (!keystoreFile.exists()) { throw new FileNotFoundException("Keystore file not found: " + keystorePath); } try (FileInputStream fis = new FileInputStream(keystoreFile)) { ks.load(fis, keystorePassword.toCharArray()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, keystorePassword.toCharArray()); SSLContext ssl = SSLContext.getInstance("TLS"); ssl.init(kmf.getKeyManagers(), null, null); return ssl; } public static void main(String[] args) throws IOException { try { System.out.println("Starting server initialization..."); Class.forName("org.sqlite.JDBC"); String keystorePath; String keystorePassword; if (args.length < 2) { System.out.println("No arguments provided, using default keystore.jks and password 'password'"); keystorePath = "keystore.jks"; keystorePassword = "password"; } else { keystorePath = args[0]; keystorePassword = args[1]; } File keystoreFile = new File(keystorePath); if (!keystoreFile.exists()) { System.out.println("Keystore file not found: " + keystorePath); System.out.println("Please generate keystore.jks file first"); return; } MessageDatabase_217230206 database = MessageDatabase_217230206.getInstance(); database.open("MessageDB.db"); System.out.println("Database opened successfully"); try { if (database.userExists("test")) { System.out.println("Database connection test passed"); } } catch (SQLException e) { System.err.println("Database connection test failed: " + e.getMessage()); } HttpsServer server = HttpsServer.create(new InetSocketAddress(8001), 0); SSLContext sslContext = myServerSSLContext(keystorePath, keystorePassword); server.setHttpsConfigurator(new HttpsConfigurator(sslContext) { @Override public void configure(HttpsParameters params) { SSLParameters sslparams = getSSLContext().getDefaultSSLParameters(); params.setSSLParameters(sslparams); } }); server.setExecutor(Executors.newCachedThreadPool()); System.out.println("Multi-threaded executor enabled"); UserAuthenticator_217230206 authenticator = new UserAuthenticator_217230206(database); HttpContext infoContext = server.createContext("/info", new RequestHandler_217230206(database)); infoContext.setAuthenticator(authenticator); server.createContext("/registration", new RegistrationHandler_217230206(authenticator, database)); server.start(); System.out.println("HTTPS Server started on port 8001"); System.out.println("Database: MessageDB.db"); System.out.println("Available endpoints:"); System.out.println("- /info (requires authentication)"); System.out.println("- /registration (user registration)"); System.out.println("Server is ready for testing"); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("Shutting down server..."); try { database.close(); } catch (SQLException e) { System.err.println("Error closing database: " + e.getMessage()); } server.stop(0); System.out.println("Server stopped gracefully"); })); } catch (FileNotFoundException e) { System.out.println("Certificate file not found!"); e.printStackTrace(); } catch (Exception e) { System.out.println("Error starting server: " + e.getMessage()); e.printStackTrace(); } } }package com.server; public class User_217230206 { private String username; private String password; private String email; private String userNickname; public User_217230206(String username, String password, String email, String userNickname) { this.username = username; this.password = password; this.email = email; this.userNickname = userNickname; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getUserNickname() { return userNickname; } public void setUserNickname(String userNickname) { this.userNickname = userNickname; } }package com.server; import com.sun.net.httpserver.BasicAuthenticator; import java.sql.SQLException; import java.util.Hashtable; import java.util.Map; public class UserAuthenticator_217230206 extends BasicAuthenticator { private Map<String, User_217230206> users = null; private MessageDatabase_217230206 database; public UserAuthenticator_217230206(MessageDatabase_217230206 database) { super("info"); this.database = database; users = new Hashtable<>(); } @Override public boolean checkCredentials(String username, String password) { try { return database.validateUser(username, password); } catch (SQLException e) { return false; } } public boolean addUser(String userName, String password, String email, String userNickname) { try { boolean dbSuccess = database.addUser(userName, password, email, userNickname); if (dbSuccess) { users.put(userName, new User_217230206(userName, password, email, userNickname)); return true; } return false; } catch (SQLException e) { return false; } } }package com.server; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; public class UserMessage_217230206 { private String locationName; private String locationDescription; private String locationCity; private long originalPostingTime; public UserMessage_217230206(String locationName, String locationDescription, String locationCity) { this.locationName = locationName; this.locationDescription = locationDescription; this.locationCity = locationCity; this.originalPostingTime = ZonedDateTime.now(ZoneId.of("UTC")).toInstant().toEpochMilli(); } public UserMessage_217230206(String locationName, String locationDescription, String locationCity, long originalPostingTime) { this.locationName = locationName; this.locationDescription = locationDescription; this.locationCity = locationCity; this.originalPostingTime = originalPostingTime; } public String getLocationName() { return locationName; } public void setLocationName(String locationName) { this.locationName = locationName; } public String getLocationDescription() { return locationDescription; } public void setLocationDescription(String locationDescription) { this.locationDescription = locationDescription; } public String getLocationCity() { return locationCity; } public void setLocationCity(String locationCity) { this.locationCity = locationCity; } public long getOriginalPostingTime() { return originalPostingTime; } public void setOriginalPostingTime(long originalPostingTime) { this.originalPostingTime = originalPostingTime; } public String getOriginalPostingTimeISO() { return ZonedDateTime.ofInstant( java.time.Instant.ofEpochMilli(originalPostingTime), ZoneId.of("UTC") ).format(DateTimeFormatter.ISO_INSTANT); } } package com.server; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; public class WeatherServiceClient_217230206 { private final String weatherServiceUrl; private final HttpClient httpClient; public WeatherServiceClient_217230206(String weatherServiceUrl) { this.weatherServiceUrl = weatherServiceUrl; this.httpClient = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(5)) .build(); } public String getWeather(double latitude, double longitude) { try { String xmlBody = String.format( "<coordinates><latitude>%f</latitude><longitude>%f</longitude></coordinates>", latitude, longitude ); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(weatherServiceUrl)) .header("Content-Type", "application/xml") .POST(HttpRequest.BodyPublishers.ofString(xmlBody)) .build(); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { return extractTemperatureFromXml(response.body()); } else { System.err.println("Weather service returned error: " + response.statusCode()); return null; } } catch (Exception e) { System.err.println("Error calling weather service: " + e.getMessage()); return null; } } private String extractTemperatureFromXml(String xmlResponse) { try { int tempStart = xmlResponse.indexOf("<temperature>") + "<temperature>".length(); int tempEnd = xmlResponse.indexOf("</temperature>"); if (tempStart > 0 && tempEnd > tempStart) { String temperature = xmlResponse.substring(tempStart, tempEnd); int unitStart = xmlResponse.indexOf("<Unit>") + "<Unit>".length(); int unitEnd = xmlResponse.indexOf("</Unit>"); String unit = unitStart > 0 && unitEnd > unitStart ? xmlResponse.substring(unitStart, unitEnd) : "C"; return temperature + unit; } } catch (Exception e) { System.err.println("Error parsing weather XML: " + e.getMessage()); } return null; } }这是已经写的代码,但是没有办法通过测试,有下面两个错误org.opentest4j.AssertionFailedError: Test failed, Object {"locationStreetAddress":"Street of D","locationName":"Place A","locationCountry":"In a C country","locationDescription":"Some random place A","latitude":1.3670745662396455,"originalPoster":"Tallaaja_123","originalPostingTime":"2025-11-18T06:52:31.570Z","locationCity":"City of B","longitude":148.53484155175929} could not be found in response ==] expected: [true] but was: [false] at com.tests.Week5.testCoordinateDescription(Week5.java:284) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)org.opentest4j.AssertionFailedError: Test failed, user registration returned code 409 ==] expected: [true] but was: [false] at com.tests.Week5.testRegisterUser(Week5.java:79) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
11-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值