Chapter 1: Spring's IOC implementation of CRUD
1.1 - Requirements and technical requirements
- Requirement: realize CRUD operation of account
- Technology:
- Using spring's IoC to manage objects
- Using dbutils as a persistence layer solution
- Using c3p0 data source
1.2 - environment construction
1.2.1-Maven project import dependency
<dependencies> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--dbUtils--> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <!--c3p0--> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <!--spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.26</version> </dependency> </dependencies>
1.2.2 - database script
CREATE DATABASE IF NOT EXISTS db1 USE db1 CREATE TABLE account( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(40), money FLOAT )CHARACTER SET utf8 COLLATE utf8_general_ci; INSERT INTO account(NAME,money) VALUES('aaa',1000); INSERT INTO account(NAME,money) VALUES('bbb',1000); INSERT INTO account(NAME,money) VALUES('ccc',1000);
1.2.3 - create entity class
public class Account { private int id; private String name; private float money; 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 float getMoney() { return money; } public void setMoney(float money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }
1.2.4 - create persistence layer
Interface
public interface IAccountDao { /** * Query all account information * * @return */ List<Account> findAll(); /** * Query a data according to id * @param id * @return */ Account findOne(int id); /** * Add a new data * @param account */ void save(Account account); /** * To update * @param account */ void update(Account account); /** * delete * @param id */ void del(int id); }
Implementation class
public class AccountDaoImpl implements IAccountDao { private QueryRunner runner = null; public void setRunner(QueryRunner runner) { this.runner = runner; } @Override public List<Account> findAll() { try{ String sql = "select * from account"; return runner.query(sql,new BeanListHandler<Account>(Account.class)); }catch (Exception e){ e.printStackTrace(); } return null; } @Override public Account findOne(int id) { try{ String sql = "select * from account where id=?"; return runner.query(sql,new BeanHandler<>(Account.class),id); }catch (Exception e){ e.printStackTrace(); } return null; } @Override public void save(Account account) { try{ String sql = "INSERT INTO account(NAME,money) VALUES(?,?)"; runner.update(sql,account.getName(),account.getMoney()); }catch (Exception e){ e.printStackTrace(); } } @Override public void update(Account account) { try{ String sql = "UPDATE account SET NAME=?,money=? WHERE id=?"; runner.update(sql,account.getName(),account.getMoney(),account.getId()); }catch (Exception e){ e.printStackTrace(); } } @Override public void del(int id) { try{ String sql = "DELETE FROM account WHERE id=?"; runner.update(sql,id); }catch (Exception e){ e.printStackTrace(); } } }
1.2.5 - create business layer
Interface
public interface IAccountServices { /** * Query all account information * * @return */ List<Account> findAll(); /** * Query a data according to id * @param id * @return */ Account findOne(int id); /** * Add a new data * @param account */ void save(Account account); /** * To update * @param account */ void update(Account account); /** * delete * @param id */ void del(int id); }
Implementation class
public class AccountServicesImpl implements IAccountServices { private IAccountDao dao = null; public void setDao(IAccountDao dao) { this.dao = dao; } @Override public List<Account> findAll() { return dao.findAll(); } @Override public Account findOne(int id) { return dao.findOne(id); } @Override public void save(Account account) { dao.save(account); } @Override public void update(Account account) { dao.update(account); } @Override public void del(int id) { dao.del(id); } }
1.2.6 - create profile
<?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"> </beans>
1.3 - configuration steps
<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"> <!--To configure IAccountService object--> <bean id="accountService" class="cn.lpl666.services.impl.AccountServicesImpl"> <property name="dao" ref="accountDao"></property> </bean> <!--To configure IAccountDao object--> <bean id="accountDao" class="cn.lpl666.dao.impl.AccountDaoImpl"> <property name="runner" ref="queryRunner"></property> </bean> <!--To configure QueryRunner--> <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"> <!--Inject data source--> <constructor-arg name="ds" ref="c3p0"></constructor-arg> </bean> <!--Configure data sources--> <bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!--Inject necessary information to connect to database--> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db1"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> </bean> </beans>
1.4- test
public class ClientTest { // Create Spring container private ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); // Get IAccountServices object private IAccountServices services = ac.getBean("accountService",IAccountServices.class); /** * Query all */ @Test public void findAll(){ List<Account> all = services.findAll(); System.out.println(all); } /** * Query one */ @Test public void findOne(){ Account account = services.findOne(1); System.out.println(account); } /** * Add to */ @Test public void save(){ Account account = new Account(); account.setName("test2"); account.setMoney(40000); services.save(account); } /** * modify */ @Test public void update(){ Account account = services.findOne(1); account.setMoney(9999); services.update(account); } /** * delete */ @Test public void del(){ services.del(6); } }
Chapter 2: IOC configuration based on annotation
2.1 - Environmental Construction
Maven introduces dependency package
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.3.RELEASE</version> </dependency>
Persistence layer interface and implementation class
Interface
public interface IAccountDao { void save(); }
Implementation class
@Component("accountDao") public class AccountDaoImpl implements IAccountDao { @Override public void save() { System.out.println("Account saved"); } }
Service layer interface and implementation class
Interface
public interface IAccountServices { void save(); }
Implementation class
@Component("accountService") @Scope("prototype") public class AccountServicesImpl implements IAccountServices { @Autowired @Qualifier("accountDao") private IAccountDao dao = null; @Value("10") private int number; @Override public void save() { dao.save(); System.out.println(number); } }
Configuration file bean.xml
<?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"> <!-- //Tell spring which packages to scan when creating containers cn.lpl666 Package all dao Layer, service layer and entity class will be scanned --> <context:component-scan base-package="cn.lpl666"/> </beans>
Test class
public class ClientUI { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); IAccountServices services =(IAccountServices) ac.getBean("accountService"); services.save(); } }
2.2 - Common notes
2.2.1 - for creating objects
Equivalent to: < bean id = "" class = "" >
@Component
Role: let spring manage the resources. This is equivalent to configuring a bean in xml.
Property: value, which specifies the id of the bean. If the value attribute is not specified, the default bean id is the class name of the current class. Initial lowercase
@Component("accountService") public class AccountServicesImpl implements IAccountServices {}
@Controller, @Service, @Repository
Their three annotations as like as two peas of @Component, are all identical.
They just provide a clearer semantics.
@Controller: annotation commonly used for presentation layer.
@Service: generally used for annotation of business layer.
@Repository: annotation commonly used for persistence layer.
Details: if there is only one attribute to assign in the annotation, and the name is value, value can not be written in the assignment.
2.2.2 - for injection data
Equivalent to: < property name = "" value = "" > or < property name = "" ref = "" >
@Autowired
Function: automatically inject by type. The set method can be omitted when using annotation injection properties. It can only inject other bean types. When there are multiple types matching, use the name of the object variable to be injected as the id of the bean, find it in the spring container, and it can also be injected successfully. If you can't find it, report it as a mistake.
@Qualifier
Function: Based on the automatic injection according to the type, it is injected according to the id of the Bean. It can't be used independently when injecting fields, it must be used together with @ Autowire; however, it can be used independently when injecting method parameters.
Property: value: Specifies the id of the bean.
@Component("accountService") public class AccountServicesImpl implements IAccountServices { @Autowired @Qualifier("accountDao") private IAccountDao dao = null; )
@Resource
Function: inject directly according to bean's id. It can only inject other bean types.
Property: name, which specifies the id of the bean.
@Component("accountService") public class AccountServicesImpl implements IAccountServices { @Resource(name="accountDao") private IAccountDao dao = null; )
@Value
Function: inject basic data type and String type data
Property: value, used to specify a value
@Value("10") private int number;
2.2.3 - for changing the range of action
Equivalent to: < bean id = "" class = "" scope = "" >
@Scope
Role: Specifies the scope of the bean.
Property: value, which specifies the value of the range.
- Value: single prototype request session global session
@Component("accountService") @Scope("prototype") public class AccountServicesImpl implements IAccountServices {}
2.2.4 - life cycle related
Equivalent to: < bean id = "" class = "" init method = "" destroy method = "" >
@PostConstruct
Function: used to specify the initialization method.
@PreDestroy
Role: used to specify the destruction method
2.2.5 - annotation and XML selection
Advantages of annotation: simple configuration and convenient maintenance (we find the class, which is equivalent to finding the corresponding configuration).
The advantage of XML: when you modify it, you don't need to change the source code. No recompilation and deployment involved
Comparison of Spring's Bean management methods:
2.3 - annotate IOC to implement CRUD
2.3.1 - Requirements and technical requirements
Ditto chapter I: 1.1 - Requirements and technical requirements
2.3.2-Maven project import dependency
<dependencies> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--dbUtils--> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <!--c3p0--> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <!--spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!--spring-test--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.3.RELEASE</version> <scope>test</scope> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.26</version> </dependency> </dependencies>
2.3.3 - database script
Ditto chapter I: 1.2.2 - database script
2.3.4 - create entity class
Ditto Chapter 1: 1.2.3 - create entity class
2.3.5 - create persistence layer
Interface:
public interface IAccountDao { /** * Query all account information * * @return */ List<Account> findAll(); /** * Query a data according to id * @param id * @return */ Account findOne(int id); /** * Add a new data * @param account */ void save(Account account); /** * To update * @param account */ void update(Account account); /** * delete * @param id */ void del(int id); }
Implementation class:
@Repository("accountDao") public class AccountDaoImpl implements IAccountDao { @Autowired private QueryRunner runner = null; @Override public List<Account> findAll() { try{ String sql = "select * from account"; return runner.query(sql,new BeanListHandler<Account>(Account.class)); }catch (Exception e){ e.printStackTrace(); } return null; } @Override public Account findOne(int id) { try{ String sql = "select * from account where id=?"; return runner.query(sql,new BeanHandler<>(Account.class),id); }catch (Exception e){ e.printStackTrace(); } return null; } @Override public void save(Account account) { try{ String sql = "INSERT INTO account(NAME,money) VALUES(?,?)"; runner.update(sql,account.getName(),account.getMoney()); }catch (Exception e){ e.printStackTrace(); } } @Override public void update(Account account) { try{ String sql = "UPDATE account SET NAME=?,money=? WHERE id=?"; runner.update(sql,account.getName(),account.getMoney(),account.getId()); }catch (Exception e){ e.printStackTrace(); } } @Override public void del(int id) { try{ String sql = "DELETE FROM account WHERE id=?"; runner.update(sql,id); }catch (Exception e){ e.printStackTrace(); } } }
2.3.6 - create business layer
Interface:
public interface IAccountServices { /** * Query all account information * * @return */ List<Account> findAll(); /** * Query a data according to id * @param id * @return */ Account findOne(int id); /** * Add a new data * @param account */ void save(Account account); /** * To update * @param account */ void update(Account account); /** * delete * @param id */ void del(int id); }
Implementation class:
@Service("accountService") @Scope("prototype") public class AccountServicesImpl implements IAccountServices { @Autowired @Qualifier("accountDao") private IAccountDao dao = null; @Override public List<Account> findAll() { return dao.findAll(); } @Override public Account findOne(int id) { return dao.findOne(id); } @Override public void save(Account account) { dao.save(account); } @Override public void update(Account account) { dao.update(account); } @Override public void del(int id) { dao.del(id); } }
2.3.7 - create jdbc configuration file
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db1 jdbc.user=root jdbc.password=root
2.3.8 - create jdbc configuration class
@PropertySource("classpath:jdbc.properties") public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.user}") private String username; @Value("${jdbc.password}") private String password; /** * Create QueryRunner object * @param ds * @return */ @Bean(name="runner") @Scope("prototype") public QueryRunner createQueryRunner(@Qualifier("dataSource") DataSource ds){ return new QueryRunner(ds); } /** * Create data source object * @return */ @Bean(name = "dataSource") public DataSource createDataSource() { try { ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setUser(username); ds.setPassword(password); ds.setDriverClass(driver); ds.setJdbcUrl(url); return ds; } catch (Exception e) { throw new RuntimeException(e); } } }
2.3.9 - create Spring configuration class
/** [Configuration] Identify that the class is a configuration class, equivalent to the bean.xml configuration file [ComponentScan] Specifies the package that spring will scan when it initializes the container. And in spring's xml configuration file: <context:component-scan base-package="cn.lpl666"/>It's the same. */ @Configuration // Indicates that the class is a configuration class @ComponentScan("cn.lpl666") // Packages to be scanned by spring container @Import({JdbcConfig.class}) // Import other configuration classes public class SpringConfiguration { }
2.3.10 - Testing
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) public class ClientTest { // Create Spring container private ApplicationContext as = null; // Get IAccountServices object @Autowired private IAccountServices services = null; /** * Query all */ @Test public void findAll(){ List<Account> all = services.findAll(); System.out.println(all); } /** * Query one */ @Test public void findOne(){ Account account = services.findOne(1); System.out.println(account); } /** * Add to */ @Test public void save(){ Account account = new Account(); account.setName("test3"); account.setMoney(40000); services.save(account); } /** * modify */ @Test public void update(){ Account account = services.findOne(1); account.setMoney(9999); services.update(account); } /** * delete */ @Test public void del(){ services.del(6); } }
2.4 - new notes
@Configuration
Function: used to specify that the current class is a spring Configuration class from which annotations will be loaded when a container is created. Annotationapplicationcontext (class with @ Configuration annotation) is required to get the container.
Property: value: bytecode used to specify configuration class
@Configuration // Indicates that the class is a configuration class public class SpringConfiguration { }
@ComponentScan
Function: used to specify the package that spring will scan when initializing the container. The function is the same as that of < context: component scan base package = "CN. Lpl666" / > in spring's xml configuration file.
Property: basePackages: used to specify the packages to scan. This is the same as the value attribute in this annotation.
@Configuration // Indicates that the class is a configuration class @ComponentScan("cn.lpl666") // Packages to be scanned by spring container public class SpringConfiguration { }
@Bean
Function: the annotation can only be written on the method, indicating that an object is created with this method and put into the spring container.
Attribute: Name: specifies a name (that is, the bean id) for the object created by the current @ bean annotation method.
public class JdbcConfig { /** * Create QueryRunner object * @param ds * @return */ @Bean(name="runner") @Scope("prototype") public QueryRunner createQueryRunner(@Qualifier("dataSource") DataSource ds){ return new QueryRunner(ds); } }
@PropertySource
Role: used to load the configuration in the. Properties file. For example, when we configure the data source, we can write the information of the connection database to the properties configuration file, and then use this annotation to specify the location of the properties configuration file.
Property: value []: used to specify the property file location. If it is under classpath, you need to write classpath:
@PropertySource("classpath:jdbc.properties") public class JdbcConfig {}
@Import
Function: used to import other Configuration classes. When importing other Configuration classes, you can no longer write @ Configuration annotation. Of course, there's no problem with that. Property: value []: used to specify the bytecode of other Configuration classes.
@Import({JdbcConfig.class}) // Import other configuration classes public class SpringConfiguration { }
2.5 - get container by annotation
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
2.6- Spring integrates Junit
2.6.1- problem
In the test class, each test method has the following two lines of code:
ApplicationContext ac =new AnnotationConfigApplicationContext(SpringConfiguration.class); IAccountService as = ac.getBean("accountService",IAccountService.class);
The purpose of these two lines of code is to get the container. If it is not written, it will directly prompt for null pointer exception. So it can't be deleted easily.
2.6.2 - solution analysis
To solve the above problems, we need the program to help us create containers automatically. Once the program can automatically create a spring container for us, we will No need to create it manually, and the problem will be solved. junit can't be implemented because it doesn't know whether we use the spring framework, let alone help us create the spring container. Fortunately, junit exposes a comment that allows us to replace its runner. At this point, we need to rely on the spring framework because it provides a runner that can read configuration files (or annotations) to create containers. I You just need to tell it where the configuration file is.
2.6.3 - configuration steps
Step 1: Maven introduces dependency package, and junit version is 4.12 or above
<!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!--spring-test--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.3.RELEASE</version> <scope>test</scope> </dependency>
Step 2: replace the original runner with the @ RunWith annotation
@RunWith(SpringJUnit4ClassRunner.class) public class ClientTest {}
Step 3: use @ ContextConfiguration to specify the location of the spring configuration file
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) // For xml configuration, use: @ ContextConfiguration(locations = "classpath:bean.xml") public class ClientTest {}
@ContextConfiguration annotation:
- locations property: used to specify the location of the configuration file.
- If it is under the classpath, you need to use classpath: to indicate the classes attribute: to specify the annotation class. When you do not use xml configuration, you need to use this property to specify the location of the annotation class
Step 4: use @ Autowired to inject data into variables in the test class
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:bean.xml") public class ClientTest { // Get IAccountServices object @Autowired private IAccountServices services = null; }
2.6.4 - why not configure the test class into xml
Before explaining this problem, let's clear up our doubts. Can we use it in XML? The answer is yes, no problem, it can be used. So why not configure to xml? This is because: First: when we configure a bean in xml and spring loads the configuration file to create the container, the object is created. Second: the test class is only used when we test the function, and it does not participate in the program logic in the project, nor solve the requirements Question, so created, not used. Then the existence of the container will cause waste of resources. Therefore, based on the above two points, we should not configure the test into an xml file.