Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the knowledge points of Spring transaction

2025-01-14 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "what are the knowledge points of Spring affairs". In the daily operation, I believe that many people have doubts about the knowledge points of Spring affairs. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the questions of "what are the knowledge points of Spring affairs?" Next, please follow the editor to study!

Overview of database transactions

First of all, a transaction is a unit of work composed of a series of operations, in which the operations are inseparable, that is, either all operations are done or none are done, which is the transaction.

Transactions must meet the characteristics of ACID (atomicity, consistency, isolation, and persistence), all of which are indispensable:

Atomicity (Atomicity): that is, a transaction is an indivisible minimum unit of work, with either all or none of the operations within the transaction.

Consistency (Consistency): before the execution of the transaction, the data of the database is in the correct state, but after the completion of the transaction, the data of the database is still in the correct state, that is, the data integrity constraint has not been broken; such as bank transfer, A transfer to B, you must ensure that A's money must be transferred to B, and A's money will not be transferred but B will not receive it, otherwise the data of the database will be in an inconsistent (incorrect) state.

Isolation (Isolation): there is no impact between concurrent transaction execution, and operations within one transaction have no impact on other transactions, which requires the transaction isolation level to specify isolation

Durability: once a transaction executes successfully, its changes to the data in the database must be permanent and will not cause data inconsistency or loss due to system failures or power outages, for example.

In actual project development, database operations are generally executed concurrently, that is, multiple transactions are executed concurrently, and concurrent execution may encounter problems. The common problems are as follows:

Lost update: two transactions update a row of data at the same time, and the update of the last transaction overwrites the update of the first transaction, resulting in the data loss of the first transaction update, which is caused by no locking.

Dirty reading: one transaction sees updated data uncommitted by another transaction

Non-repeatable reading: in the same transaction, the same data is read multiple times but different results are returned; that is, other transactions change the data

Illusion: during execution, one transaction reads the inserted data committed by another transaction; that is, a batch of data is read at the beginning of the first transaction, but then another transaction inserts the new data and commits it. At this time, the first transaction reads the data but finds that there is one more item, that is, as if a hallucination had occurred.

In order to solve these concurrency problems, database isolation levels are required, and four isolation levels are defined in the standard SQL specification:

Uncommitted read (Read Uncommitted): at the lowest isolation level, a transaction can read uncommitted update data from other transactions, which is very insecure, and may cause missing updates, dirty reads, unrepeatable reads, and phantom reads.

Commit read (Read Committed): a transaction can read the update data submitted by another transaction, can not see the uncommitted update data, may not have lost updates or dirty reads, but may have unrepeatable reads and phantom readings.

Repeatable Read: guarantees that multiple queries executed successively in the same transaction will return the same result, unaffected by other transactions, and may cause missing updates, dirty reads, non-repeatable reads, but phantom readings

Serialization (Serializable): the highest isolation level, does not allow transactions to execute concurrently, but must be serialized, the most secure, there can be no updates, dirty reads, non-repeatable reads, phantom reads.

The higher the isolation level, the worse the concurrent execution performance of database transactions, and the less operations can be handled. Therefore, in the actual project development, the commit read isolation level is generally used to consider the concurrency performance, which can avoid the loss of updates and dirty reads, although unrepeatable reads and phantom reads cannot be avoided. However, pessimistic locks or optimistic locks can be used to solve these problems where possible.

Transaction type

Database transaction types include local transactions and distributed transactions:

Local transactions: ordinary transactions, ACID that can guarantee operations on a single database, are limited to a single database.

Distributed transactions: transactions involving two or more database sources, that is, transactions that span multiple homogeneous or heterogeneous databases (consisting of local transactions of each database). Distributed transactions are designed to guarantee the ACID of all operations of these local transactions, so that transactions can span multiple databases

Java transaction types are JDBC transaction and JTA transaction:

JDBC transaction: the local transaction in the database transaction type, which is managed through the control of the Connection object

JTA transaction: JTA refers to Java transaction API (Java Transaction API), which is the Java EE database transaction specification. JTA only provides transaction management interface and is implemented by application server vendors (such as WebSphere Application Server). JTA transaction is more powerful than JDBC and supports distributed transactions.

Java EE transaction types are local and global transactions:

Local transactions: using JDBC programming to implement transactions

Global transactions: provided by the application server, using JTA transactions

There are declarative and programmatic transactions according to whether to implement transactions programmatically or not.

Declarative transactions: specify transaction information through annotations or XML configuration files

Programmatic transactions: transactions are implemented by writing code.

Transaction management provided by Spring

One of the core functions of the Spring framework is transaction management, and it provides a consistent transaction management abstraction, which helps us:

Provides a consistent programmatic transaction management API, whether using the Spring JDBC framework or the integrated third-party framework for transaction programming using the API

Non-invasive declarative transaction support.

Spring supports declarative and programmatic transaction types.

Spring transaction characteristics

All spring transaction management policy classes inherit from the org.springframework.transaction.PlatformTransactionManager interface

The TransactionDefinition interface defines the following features:

Transaction isolation level

The isolation level refers to the degree of isolation between several concurrent transactions. Five constants representing the isolation level are defined in the TransactionDefinition interface:

TransactionDefinition.ISOLATION_DEFAULT: this is the default value, which means that the default isolation level of the underlying database is used. For most databases, this value is usually TransactionDefinition.ISOLATION_READ_COMMITTED.

TransactionDefinition.ISOLATION_READ_UNCOMMITTED: this isolation level means that one transaction can read data modified by another transaction that has not yet been committed. This level does not prevent dirty, non-repeatable and phantom reads, so the isolation level is rarely used. For example, PostgreSQL does not actually have this level.

TransactionDefinition.ISOLATION_READ_COMMITTED: this isolation level means that one transaction can only read data that another transaction has committed. This level prevents dirty reading, which is the recommended value in most cases.

TransactionDefinition.ISOLATION_REPEATABLE_READ: this isolation level means that a transaction can execute a query repeatedly throughout the process, and the records returned each time are the same. This level prevents dirty reading and non-repeatable reading.

TransactionDefinition.ISOLATION_SERIALIZABLE: all transactions are executed one by one, so that interference between transactions is completely impossible, that is, this level prevents dirty reads, unrepeatable reads, and phantom reads. But this will seriously affect the performance of the program. This level is not usually used.

Transaction propagation behavior means that if a transaction context already exists before the start of the current transaction, there are several options to specify the execution behavior of a transactional method. The following constants that represent propagation behavior are included in the TransactionDefinition definition:

TransactionDefinition.PROPAGATION_REQUIRED: if there is a transaction, join it; if there is no transaction, create a new transaction. This is the default value.

TransactionDefinition.PROPAGATION_REQUIRES_NEW: creates a new transaction and suspends the current transaction if it exists.

TransactionDefinition.PROPAGATION_SUPPORTS: if there is a transaction, join it; if there is no transaction, continue to run in a non-transactional manner.

TransactionDefinition.PROPAGATION_NOT_SUPPORTED: runs in a non-transactional manner, suspending the current transaction if there is a current transaction.

TransactionDefinition.PROPAGATION_NEVER: runs in a non-transactional manner, throwing an exception if a transaction currently exists.

TransactionDefinition.PROPAGATION_MANDATORY: if there is a transaction, join it; if there is no transaction, throw an exception.

TransactionDefinition.PROPAGATION_NESTED: if there is a transaction, a transaction is created to run as a nested transaction of the current transaction; if there is no transaction, this value is equivalent to TransactionDefinition.PROPAGATION_REQUIRED.

Transaction timeout refers to the maximum time a transaction is allowed to execute. If the time limit is exceeded but the transaction has not been completed, the transaction is automatically rolled back. In TransactionDefinition, the timeout is represented by the value of int, in seconds.

The default setting is the timeout value of the underlying transaction system. If the underlying database transaction system does not set the timeout value, then it is none, and there is no timeout limit.

Transaction read-only properties read-only transactions are used in situations where client code is read-only but do not modify data, and read-only transactions are used for optimization in specific scenarios, such as when using Hibernate.

The default is a read-write transaction.

Overview

The core of Spring framework supporting transaction management is transaction manager abstraction. Different data access frameworks (such as Hibernate) can support transaction management of various data access frameworks by implementing policy interface PlatformTransactionManager. PlatformTransactionManager interface is defined as follows:

Java Code:

Public interface PlatformTransactionManager {TransactionStatus getTransaction (TransactionDefinition definition) throws TransactionException; void commit (TransactionStatus status) throws TransactionException; void rollback (TransactionStatus status) throws TransactionException;}

GetTransaction (): returns an active transaction or creates a new transaction (based on the transaction properties defined by the given TransactionDefinition type parameter). The return is that the TransactionStatus object represents the state of the current transaction, where the method throws a TransactionException (unchecked exception) indicating that the transaction failed for some reason.

Commit (): used to submit the transaction represented by the TransactionStatus parameter. For more information, please see Spring Javadoc.

Rollback (): used to roll back the transaction represented by the TransactionStatus parameter. For more information, please see Spring Javadoc.

The TransactionDefinition API is defined as follows:

Java Code:

Public interface TransactionDefinition {int getPropagationBehavior (); int getIsolationLevel (); int getTimeout (); boolean isReadOnly (); String getName ();}

GetPropagationBehavior (): returns the defined transaction propagation behavior

GetIsolationLevel (): returns the defined transaction isolation level

GetTimeout (): returns the defined transaction timeout

IsReadOnly (): returns whether the defined transaction is read-only

GetName (): returns the name of the defined transaction.

The TransactionStatus API is defined as follows:

Java Code:

Public interface TransactionStatus extends SavepointManager {boolean isNewTransaction (); boolean hasSavepoint (); void setRollbackOnly (); boolean isRollbackOnly (); void flush (); boolean isCompleted ();}

IsNewTransaction (): returns whether the current transaction status is a new transaction

HasSavepoint (): returns whether the current transaction has a SavePoint

SetRollbackOnly (): sets that the current transaction should be rolled back

IsRollbackOnly ((): returns whether the current transaction should be rolled back

Flush (): used to refresh modifications in the underlying session to the database, generally used to refresh sessions such as Hibernate/JPA, and may have no effect on transactions such as JDBC

IsCompleted (): whether the current transaction has been completed.

Built-in transaction manager implementation

Spring provides many built-in transaction manager implementations:

DataSourceTransactionManager: located in the org.springframework.jdbc.datasource package, a data source transaction manager that provides management of a single javax.sql.DataSource transaction for transaction management of the Spring JDBC abstract framework, iBATIS, or MyBatis framework

JdoTransactionManager: located in the org.springframework.orm.jdo package, provides management of a single javax.jdo.PersistenceManagerFactory transaction for transaction management when integrating the JDO framework

JpaTransactionManager: located in the org.springframework.orm.jpa package, provides support for a single javax.persistence.EntityManagerFactory transaction for transaction management when integrating the JPA implementation framework

HibernateTransactionManager: located in the org.springframework.orm.hibernate3 package, provides support for a single org.hibernate.SessionFactory transaction for transaction management when integrating the Hibernate framework; this transaction manager only supports the Hibernate3+ version, and the Spring3.0+ version only supports Hibernate 3.2 +

JtaTransactionManager: located in the org.springframework.transaction.jta package, provides support for distributed transaction management and delegates transaction management to the Java EE application server transaction manager

OC4JjtaTransactionManager: an adapter provided by Spring in the org.springframework.transaction.jta package to the OC4J10.1.3+ application server transaction manager, which is used to support advanced transactions provided by the application server

WebSphereUowTransactionManager: adapter provided by Spring in the org.springframework.transaction.jta package to the WebSphere 6.0 + application server transaction manager, which is used to support advanced transactions provided by the application server

WebLogicJtaTransactionManager: located in the org.springframework.transaction.jta package, Spring provides an adapter for the WebLogic 8.1 + application server transaction manager, which is used to support advanced transactions provided by the application server.

Spring provides not only these transaction managers, but also for managers such as JMS transaction management, Spring provides a consistent transaction abstraction as shown in figure 9-1.

Figure 9-1 Spring transaction Manager

Next, let's learn how to define the transaction manager in the Spring configuration file:

First, declare support for local transactions:

A) JDBC and iBATIS, MyBatis framework transaction managers

Java Code:

Specify a single javax.sql.DataSource object that requires transaction management through the dataSource property.

B) Jdo transaction Manager

Java Code:

Specify the javax.jdo.PersistenceManagerFactory object that requires transaction management through the persistenceManagerFactory property.

C) Jpa transaction Manager

Java Code:

Bean id= "txManager" class= "org.springframework.orm.jpa.JpaTransactionManager" >

Specify the javax.persistence.EntityManagerFactory object that requires transaction management through the entityManagerFactory property.

You also need to specify the jpaDialect property for the entityManagerFactory object, which corresponds to how to get the connection object, open the transaction, close the transaction, and other transaction management-related behaviors.

Java Code:

……

D) Hibernate transaction Manager

Java Code:

Specify the org.hibernate.SessionFactory object that requires transaction management through the entityManagerFactory property.

Declarative transaction Overview

From the previous section of programming to achieve transaction management, you can deeply understand the pain of programming transactions, even through proxy configuration is not a small amount of work.

This section introduces declarative transaction support, the biggest benefit of which is simplicity, transaction management is no longer painful, and this approach is non-intrusive and has no impact on business logic implementation.

Let's take a look at how declarative transactions are implemented.

Declarative implementation of transaction management

1. Define the business logic implementation, where ConfigUserServiceImpl and ConfigAddressServiceImpl are used:

2. Define the configuration file (chapter9/service/ applicationContext-service-declare.xml):

2.1.Definitions of XML namespaces, which define tx namespaces for transaction support and aop namespaces supported by AOP:

Java Code:

2.2. Business implementation configuration, which is very simple, using the previously defined non-intrusive business implementation:

Java Code:

2.3. Transaction-related configuration:

Java Code:

Java Code:

Transaction notification definition, used to specify transaction attributes, where the "transaction-manager" attribute specifies the transaction manager, and through the

< tx:attributes >

Specify the specific method to be intercepted;: means that the method starting with save will be intercepted, and the intercepted method will apply the configured transaction attribute: propagation= "REQUIRED" indicates that the propagation behavior is Required,isolation= "READ_COMMITTED", indicating that the isolation level is committed read. : indicates that all other methods will be intercepted, and the intercepted method will apply the configured transaction attributes: propagation= "REQUIRED" indicates that the propagation behavior is Required,isolation= "READ_COMMITTED" means that the isolation level is committed read, and read-only= "true" indicates transaction read only;: AOP related configuration:: pointcut definition, defines an aspectj pointcut named "serviceMethod", and the pointcut expression is "execution (* cn..chapter9.service..*.* (..)"). It means to intercept the chapter9 under the cn package and its subpackages. Any method of any class under the service package and subpackage;: Advisor definition, where the pointcut is serviceMethod and the notification is txAdvice. As you can see from the configuration, the cn package and the chapter9 under the subpackage will be checked. Any method of any class under the service package and subpackage applies the transaction attribute specified by the "txAdvice" notification.

3. Modify the test method and test whether the configuration method is easy to use:

Make a copy of the testServiceTransaction test method of the TransactionTest class and name it testDeclareTransaction:

And within the testDeclareTransaction test method:

4. Execute the test, and the test passes normally, indicating that this method can work normally. When calling the save method, it will match to the transaction attribute specified in "" defined in the transaction notification, while when calling the countAll method, it will match to the transaction attribute specified in "" defined in the transaction notification. "

How do declarative transactions manage transactions? Do you still remember that TransactionProxyFactoryBean implements configuration transaction management, configuration transaction management is implemented through agents, and declarative transaction management is also implemented through AOP agents.

Declarative transactions realize transaction management through AOP proxy, and use surround notification TransactionInterceptor to open and close transactions, and the interior of TransactionProxyFactoryBean is also realized through this surround notification, so it can be considered to help you define TransactionProxyFactoryBean, thus simplifying transaction management.

Now that you know how to implement it, let's learn about the configuration in detail:

9.4.4 configuration details

Declarative transaction management defines transaction attributes through configuration, as follows:

Java code:... : id is used to specify the name of this notification, transaction-manager is used to specify the transaction manager, and the default transaction manager name is "transactionManager";: used to define the transaction property, that is, the associated method name

Name: defines the name of the method associated with the transaction attribute, which will apply the defined transaction attribute to the matching method. You can use the "" wildcard "to match one or all of the methods. For example," save "will match the method that starts with save, while" * "will match all the methods.

Propagation: transaction propagation behavior definition. Default is "REQUIRED", which means Required. Its value can be specified after "PROPAGATION_" of static propagation behavior variable of TransactionDefinition. For example, "TransactionDefinition.PROPAGATION_REQUIRED" can be specified using "REQUIRED".

Isolation: transaction isolation level definition; the default is "DEFAULT", and its value can be specified by the "ISOLATION_" section of the static isolation level variable of TransactionDefinition. For example, "TransactionDefinition. ISOLATION_DEFAULT" can be specified using "DEFAULT":

Timeout: transaction timeout setting (in seconds). Default is-1, indicating that the transaction timeout will depend on the underlying transaction system.

Read-only: read-only setting for transactions. Default is false, which means it is not read-only.

Rollback-for: the exception definition that triggers the rollback is required to split with ",". By default, any RuntimeException will cause the transaction to be rolled back, while any Checked Exception will not cause the transaction to be rolled back. The exception name definition has the same meaning as in TransactionProxyFactoryBean.

No-rollback-for: Exception (s) that is not triggered for rollback; split by ","; exception name definition has the same meaning as in TransactionProxyFactoryBean

Do you remember that in order to solve the problem of not setting the correct transaction properties caused by "self-invocation" in the configuration mode, use the "((IUserService) AopContext.currentProxy ()). OtherTransactionMethod ()" method, which is needed to open the declarative transaction to be supported.

9.4.5 Multi-transaction semantic configuration and Best practices

What is multi-transaction semantics? To put it bluntly, it is to configure different transaction properties for different Bean, because there may not be a few Bean in our project, but there may be many, which may require grouping Bean and configuring different transaction semantics for different groups of Bean. In Spring, you can configure multi-pointcut and multi-transaction notifications and combine them in different ways.

1. First, take a look at the best practices for declarative transaction configuration:

The declarative transaction configuration can cope with the common CRUD interface definition and implement transaction management. We only need to modify the pointcut expression to intercept our business implementation and apply transaction attributes to it. If there are more complex transaction attributes, we can add them directly.

If we have a batchSaveOrUpdate method that requires "REQUIRES_NEW" transaction propagation behavior, we can simply add the following configuration:

Java Code:

one

2. Next, let's take a look at the multi-transaction semantic configuration. Common transaction attributes are already configured in the declarative transaction best practices, so you can make special configuration for business methods that require other transaction attributes:

Java Code:

This declaration applies the "Never" propagation behavior to the transaction where the pointcut matches the method.

When configuring multi-transaction semantics, pointcuts must not be superimposed, otherwise transaction attributes will be applied twice, resulting in unnecessary errors and troubles.

@ Transactional implements transaction management

For declarative transaction management, Spring provides an annotation based on @ Transactional, but requires Java 5 +.

Annotations are the easiest way to configure transactions. You can declare transaction attributes directly in the Java source code, and you must use this annotation for each business class or method if you need a transaction.

Let's learn about the use of annotated transactions:

1. Define the business logic implementation:

Package cn.javass.spring.chapter9.service.impl;// omits importpublic class AnnotationUserServiceImpl implements IUserService {private IUserDao userDao; private IAddressService addressService; public void setUserDao (IUserDao userDao) {this.userDao = userDao;} public void setAddressService (IAddressService addressService) {this.addressService = addressService;} @ Transactional (propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED) @ Override public void save (final UserModel user) {userDao.save (user) User.getAddress () .setUserId (user.getId ()); addressService.save (user.getAddress ());} @ Transactional (propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED, readOnly=true) @ Override public int countAll () {return userDao.countAll ();}}

2. Define the configuration file (chapter9/service/ applicationContext-service-annotation.xml):

2.1.Definitions of XML namespaces, which define tx namespaces for transaction support and aop namespaces supported by AOP:

Java Code:

2.2. Business implementation configuration, which is very simple, using the previously defined non-intrusive business implementation:

Java Code:

2.3. Transaction-related configuration:

Java code: 1 declarative transactions are supported using the configuration above. 3. Modify the test method and test whether the configuration is easy to use: make a copy of the testServiceTransaction test method of the TransactionTest class named testAnntationTransactionTest:classpath:chapter9/service/applicationContext-service-annotation.xml "userService.save (user); try {userService.save (user); Assert.fail ();} catch (RuntimeException e) {} Assert.assertEquals (0, userService.countAll ()); Assert.assertEquals (0, addressService.countAll ())

4. Execute the test, and the test passes normally, indicating that this method can work properly. Because an exception is thrown in the save method of the AnnotationAddressServiceImpl class, the transaction needs to be rolled back, so both countAll operations return 0.

9.4.7 @ Transactional configuration details

Spring provides support for annotation transaction management to identify @ Transactional annotation metadata on the Bean class, which has the following properties:

Transaction-manager: specify the name of the transaction manager. The default is transactionManager. You need to specify it explicitly when using another name.

Proxy-target-class: indicates the code mechanism to be used. The default false means to use the JDK proxy, and if true, the CGLIB proxy will be used.

Order: defines the order of transaction notification. The default Ordered.LOWEST_PRECEDENCE means that the order decision is handed over to AOP.

Spring uses @ Transactional to specify transaction attributes, which can be specified on interfaces, classes, or methods. If @ Transactional is specified on both classes and methods, the transaction attributes on methods take precedence, as shown below:

Value: specifies the name of the transaction manager, which is used by default to support a multi-transaction manager environment.

Propagation: specify transaction propagation behavior. Default is Required, and Propagation.REQUIRED is used.

Isolation: specifies the transaction isolation level, which defaults to "DEFAULT" and is specified using Isolation.DEFAULT

ReadOnly: specifies whether the transaction is read-only. The default false means that the transaction is not read-only.

Timeout: specifies the transaction timeout in seconds. The default of-1 means that the transaction timeout will depend on the underlying transaction system.

RollbackFor: specify a set of exception classes, which will roll back the transaction if encountered

RollbackForClassname: specifies a set of exception class names that have exactly the same meaning as the rollback-for attribute semantics in

NoRollbackFor: specifies a set of exception classes that commit the transaction even if an exception is encountered, that is, the transaction is not rolled back

NoRollbackForClassname: specifies a set of exception class names that have exactly the same meaning as the no-rollback-for attribute semantics in

The @ Transactional annotation provided by Spring also uses surround notification TransactionInterceptor to open and close transactions internally.

Using @ Transactional to annotate transaction management requires special attention to the following:

If @ Transactional annotations are specified on an interface, implementation class, or method, the priority order is method > implementation class > interface

It is recommended to use @ Transactional only on implementing classes or methods of implementing classes, not on interfaces, because it is fine to use the JDK proxy mechanism because it uses interface-based proxies, while using the CGLIB proxy mechanism will encounter problems because it uses class-based proxies instead of interfaces, because the @ Transactional annotation on the interface is "not inheritable"

For details, please refer to the difference between the implementation of Spring annotation management transactions (@ Trasactional) based on JDK dynamic agents and CGLIB dynamic agents.

Under the Spring proxy mechanism (whether it is a JDK dynamic proxy or a CGLIB proxy), self-invocation also does not apply the corresponding transaction attribute, and its semantics are the same as in

Only RuntimeException exceptions are rolled back by default

When using the Spring proxy, the @ Transactional annotation of the public visibility method is valid by default, even if there is @ Transactional on other visibility methods (protected, private, package visibility).

Annotations will not apply these transaction attributes, and Spring will not report errors, so if you have to use non-public methods to annotate transaction management, consider using AspectJ.

At this point, the study of "what are the knowledge points of Spring affairs" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report