Merge pull request #2211 from eugenp/bael-951-responsive-services
bael-951-responsive-services
This commit is contained in:
		
						commit
						026c9717ce
					
				| @ -1,78 +1,87 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
| 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
| 	<modelVersion>4.0.0</modelVersion> | ||||
| 
 | ||||
|     <groupId>com.baeldung.spring.cloud</groupId> | ||||
|     <artifactId>svc-rating</artifactId> | ||||
|     <version>1.0.0-SNAPSHOT</version> | ||||
| 	<groupId>com.baeldung.spring.cloud</groupId> | ||||
| 	<artifactId>svc-rating</artifactId> | ||||
| 	<version>1.0.0-SNAPSHOT</version> | ||||
| 
 | ||||
|     <parent> | ||||
|         <artifactId>parent-boot-4</artifactId> | ||||
|         <groupId>com.baeldung</groupId> | ||||
|         <version>0.0.1-SNAPSHOT</version> | ||||
|         <relativePath>../../../parent-boot-4</relativePath> | ||||
|     </parent> | ||||
| 	<parent> | ||||
| 		<artifactId>parent-boot-4</artifactId> | ||||
| 		<groupId>com.baeldung</groupId> | ||||
| 		<version>0.0.1-SNAPSHOT</version> | ||||
| 		<relativePath>../../../parent-boot-4</relativePath> | ||||
| 	</parent> | ||||
| 
 | ||||
|     <dependencies> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.cloud</groupId> | ||||
|             <artifactId>spring-cloud-starter-config</artifactId> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.cloud</groupId> | ||||
|             <artifactId>spring-cloud-starter-eureka</artifactId> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-web</artifactId> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-security</artifactId> | ||||
|         </dependency> | ||||
| 	<dependencies> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.cloud</groupId> | ||||
| 			<artifactId>spring-cloud-starter-config</artifactId> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.cloud</groupId> | ||||
| 			<artifactId>spring-cloud-starter-eureka</artifactId> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.boot</groupId> | ||||
| 			<artifactId>spring-boot-starter-web</artifactId> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.boot</groupId> | ||||
| 			<artifactId>spring-boot-starter-security</artifactId> | ||||
| 		</dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.session</groupId> | ||||
|             <artifactId>spring-session</artifactId> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-data-redis</artifactId> | ||||
|         </dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.session</groupId> | ||||
| 			<artifactId>spring-session</artifactId> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.boot</groupId> | ||||
| 			<artifactId>spring-boot-starter-data-redis</artifactId> | ||||
| 		</dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-data-jpa</artifactId> | ||||
|         </dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.boot</groupId> | ||||
| 			<artifactId>spring-boot-starter-data-jpa</artifactId> | ||||
| 		</dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>com.h2database</groupId> | ||||
|             <artifactId>h2</artifactId> | ||||
|             <scope>runtime</scope> | ||||
|         </dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.cloud</groupId> | ||||
| 			<artifactId>spring-cloud-starter-hystrix</artifactId> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.boot</groupId> | ||||
| 			<artifactId>spring-boot-starter-actuator</artifactId> | ||||
| 		</dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.cloud</groupId> | ||||
|             <artifactId>spring-cloud-starter-zipkin</artifactId> | ||||
|         </dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>com.h2database</groupId> | ||||
| 			<artifactId>h2</artifactId> | ||||
| 			<scope>runtime</scope> | ||||
| 		</dependency> | ||||
| 
 | ||||
|     </dependencies> | ||||
| 		<dependency> | ||||
| 			<groupId>org.springframework.cloud</groupId> | ||||
| 			<artifactId>spring-cloud-starter-zipkin</artifactId> | ||||
| 		</dependency> | ||||
| 
 | ||||
|     <dependencyManagement> | ||||
|         <dependencies> | ||||
|             <dependency> | ||||
|                 <groupId>org.springframework.cloud</groupId> | ||||
|                 <artifactId>spring-cloud-dependencies</artifactId> | ||||
|                 <version>${spring-cloud-dependencies.version}</version> | ||||
|                 <type>pom</type> | ||||
|                 <scope>import</scope> | ||||
|             </dependency> | ||||
|         </dependencies> | ||||
|     </dependencyManagement> | ||||
| 	</dependencies> | ||||
| 
 | ||||
| 	<dependencyManagement> | ||||
| 		<dependencies> | ||||
| 			<dependency> | ||||
| 				<groupId>org.springframework.cloud</groupId> | ||||
| 				<artifactId>spring-cloud-dependencies</artifactId> | ||||
| 				<version>${spring-cloud-dependencies.version}</version> | ||||
| 				<type>pom</type> | ||||
| 				<scope>import</scope> | ||||
| 			</dependency> | ||||
| 		</dependencies> | ||||
| 	</dependencyManagement> | ||||
| 
 | ||||
| 	<properties> | ||||
| 		<spring-cloud-dependencies.version>Brixton.SR7</spring-cloud-dependencies.version> | ||||
| 	</properties> | ||||
| 
 | ||||
|     <properties> | ||||
|         <spring-cloud-dependencies.version>Brixton.SR7</spring-cloud-dependencies.version> | ||||
|     </properties> | ||||
|      | ||||
| </project> | ||||
| @ -1,21 +1,32 @@ | ||||
| package com.baeldung.spring.cloud.bootstrap.svcrating; | ||||
| 
 | ||||
| import com.netflix.appinfo.InstanceInfo; | ||||
| import com.netflix.discovery.EurekaClient; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.boot.SpringApplication; | ||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||
| import org.springframework.cloud.netflix.eureka.EnableEurekaClient; | ||||
| import org.springframework.cloud.netflix.hystrix.EnableHystrix; | ||||
| import org.springframework.cloud.sleuth.metric.SpanMetricReporter; | ||||
| import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter; | ||||
| import org.springframework.cloud.sleuth.zipkin.ZipkinProperties; | ||||
| import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter; | ||||
| import org.springframework.context.annotation.AdviceMode; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Primary; | ||||
| import org.springframework.core.Ordered; | ||||
| import org.springframework.core.annotation.Order; | ||||
| import org.springframework.transaction.annotation.EnableTransactionManagement; | ||||
| 
 | ||||
| import com.netflix.appinfo.InstanceInfo; | ||||
| import com.netflix.discovery.EurekaClient; | ||||
| import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect; | ||||
| 
 | ||||
| import zipkin.Span; | ||||
| 
 | ||||
| @SpringBootApplication | ||||
| @EnableEurekaClient | ||||
| @EnableHystrix | ||||
| @EnableTransactionManagement(order=Ordered.LOWEST_PRECEDENCE, mode=AdviceMode.ASPECTJ) | ||||
| public class RatingServiceApplication { | ||||
|     @Autowired | ||||
|     private EurekaClient eurekaClient; | ||||
| @ -48,4 +59,11 @@ public class RatingServiceApplication { | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|      | ||||
|     @Bean | ||||
|     @Primary | ||||
|     @Order(value=Ordered.HIGHEST_PRECEDENCE) | ||||
|     public HystrixCommandAspect hystrixAspect() { | ||||
|       return new HystrixCommandAspect(); | ||||
|     } | ||||
| } | ||||
| @ -20,17 +20,20 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { | ||||
| 
 | ||||
|     @Override | ||||
|     protected void configure(HttpSecurity http) throws Exception { | ||||
|         http.httpBasic() | ||||
|             .disable() | ||||
|         http | ||||
|         .authorizeRequests() | ||||
|             .regexMatchers("^/ratings\\?bookId.*$").authenticated() | ||||
|             .antMatchers(HttpMethod.POST,"/ratings").authenticated() | ||||
|             .antMatchers(HttpMethod.PATCH,"/ratings/*").hasRole("ADMIN") | ||||
|             .antMatchers(HttpMethod.DELETE,"/ratings/*").hasRole("ADMIN") | ||||
|             .antMatchers(HttpMethod.GET,"/ratings").hasRole("ADMIN") | ||||
|             .antMatchers(HttpMethod.GET,"/hystrix").authenticated() | ||||
|             .anyRequest().authenticated() | ||||
|             .and() | ||||
|          .httpBasic().and()    | ||||
|         .csrf() | ||||
|             .disable(); | ||||
|         | ||||
|          | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,10 +1,28 @@ | ||||
| package com.baeldung.spring.cloud.bootstrap.svcrating; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.context.annotation.Primary; | ||||
| import org.springframework.core.env.Environment; | ||||
| import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; | ||||
| import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; | ||||
| import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer; | ||||
| 
 | ||||
| @Configuration | ||||
| @EnableRedisHttpSession | ||||
| public class SessionConfig extends AbstractHttpSessionApplicationInitializer { | ||||
|     @Autowired | ||||
|     Environment properties; | ||||
|      | ||||
|     @Bean | ||||
|     @Primary | ||||
|     public JedisConnectionFactory connectionFactory() { | ||||
|         JedisConnectionFactory factory = new JedisConnectionFactory(); | ||||
|         factory.setHostName(properties.getProperty("spring.redis.host","localhost")); | ||||
|         factory.setPort(properties.getProperty("spring.redis.port", Integer.TYPE,6379)); | ||||
|         factory.afterPropertiesSet(); | ||||
|         factory.setUsePool(true); | ||||
|         return factory; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,22 +1,34 @@ | ||||
| package com.baeldung.spring.cloud.bootstrap.svcrating.rating; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||||
| import java.io.Serializable; | ||||
| 
 | ||||
| import javax.persistence.Entity; | ||||
| import javax.persistence.GeneratedValue; | ||||
| import javax.persistence.GenerationType; | ||||
| import javax.persistence.Id; | ||||
| import javax.persistence.Transient; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||||
| 
 | ||||
| @Entity | ||||
| @JsonIgnoreProperties(ignoreUnknown = true) | ||||
| public class Rating { | ||||
| public class Rating implements Serializable { | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      */ | ||||
|     private static final long serialVersionUID = 3308900941650386473L; | ||||
|     @Id | ||||
|     @GeneratedValue(strategy = GenerationType.AUTO) | ||||
|     private Long id; | ||||
|     private Long bookId; | ||||
|     private int stars; | ||||
| 
 | ||||
|     @Transient | ||||
|     private boolean fromCache; | ||||
|     @Transient | ||||
|     private Long cachedTS = -1L; | ||||
| 
 | ||||
|     public Rating() { | ||||
|     } | ||||
| 
 | ||||
| @ -54,4 +66,21 @@ public class Rating { | ||||
|     public void setStars(int stars) { | ||||
|         this.stars = stars; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isFromCache() { | ||||
|         return fromCache; | ||||
|     } | ||||
| 
 | ||||
|     public void setFromCache(boolean fromCache) { | ||||
|         this.fromCache = fromCache; | ||||
|     } | ||||
| 
 | ||||
|     public Long getCachedTS() { | ||||
|         return cachedTS; | ||||
|     } | ||||
| 
 | ||||
|     public void setCachedTS(Long cachedTS) { | ||||
|         this.cachedTS = cachedTS; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,118 @@ | ||||
| package com.baeldung.spring.cloud.bootstrap.svcrating.rating; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; | ||||
| import org.springframework.data.redis.core.SetOperations; | ||||
| import org.springframework.data.redis.core.StringRedisTemplate; | ||||
| import org.springframework.data.redis.core.ValueOperations; | ||||
| 
 | ||||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||||
| import com.fasterxml.jackson.databind.DeserializationFeature; | ||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||
| import org.springframework.stereotype.Repository; | ||||
| 
 | ||||
| @Repository | ||||
| public class RatingCacheRepository implements InitializingBean { | ||||
| 
 | ||||
|     @Autowired | ||||
|     private JedisConnectionFactory cacheConnectionFactory; | ||||
| 
 | ||||
|     private StringRedisTemplate redisTemplate; | ||||
|     private ValueOperations<String, String> valueOps; | ||||
|     private SetOperations<String, String> setOps; | ||||
| 
 | ||||
|     private ObjectMapper jsonMapper; | ||||
| 
 | ||||
|     public List<Rating> findCachedRatingsByBookId(Long bookId) { | ||||
|         return setOps.members("book-" + bookId) | ||||
|             .stream() | ||||
|             .map(rtId -> { | ||||
|                 try { | ||||
|                     return jsonMapper.readValue(valueOps.get(rtId), Rating.class); | ||||
|                 } catch (IOException ex) { | ||||
|                     return null; | ||||
|                 } | ||||
|             }) | ||||
|             .collect(Collectors.toList()); | ||||
|     } | ||||
| 
 | ||||
|     public Rating findCachedRatingById(Long ratingId) { | ||||
| 
 | ||||
|         try { | ||||
|             return jsonMapper.readValue(valueOps.get("rating-" + ratingId), Rating.class); | ||||
|         } catch (IOException e) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public List<Rating> findAllCachedRatings() { | ||||
|         List<Rating> ratings = null; | ||||
| 
 | ||||
|         ratings = redisTemplate.keys("rating*") | ||||
|             .stream() | ||||
|             .map(rtId -> { | ||||
|                 try { | ||||
| 
 | ||||
|                     return jsonMapper.readValue(valueOps.get(rtId), Rating.class); | ||||
| 
 | ||||
|                 } catch (IOException e) { | ||||
|                     return null; | ||||
|                 } | ||||
|             }) | ||||
|             .collect(Collectors.toList()); | ||||
| 
 | ||||
|         return ratings; | ||||
|     } | ||||
| 
 | ||||
|     public boolean createRating(Rating persisted) { | ||||
|         try { | ||||
|             valueOps.set("rating-" + persisted.getId(), jsonMapper.writeValueAsString(persisted)); | ||||
|             setOps.add("book-" + persisted.getBookId(), "rating-" + persisted.getId()); | ||||
|             return true; | ||||
|         } catch (JsonProcessingException ex) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public boolean updateRating(Rating persisted) { | ||||
|         try { | ||||
|             valueOps.set("rating-" + persisted.getId(), jsonMapper.writeValueAsString(persisted)); | ||||
|             return true; | ||||
|         } catch (JsonProcessingException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     public boolean deleteRating(Long ratingId) { | ||||
|         Rating toDel; | ||||
|         try { | ||||
| 
 | ||||
|             toDel = jsonMapper.readValue(valueOps.get("rating-" + ratingId), Rating.class); | ||||
|             setOps.remove("book-" + toDel.getBookId(), "rating-" + ratingId); | ||||
|             redisTemplate.delete("rating-" + ratingId); | ||||
|             return true; | ||||
|         } catch (IOException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void afterPropertiesSet() throws Exception { | ||||
| 
 | ||||
|         this.redisTemplate = new StringRedisTemplate(cacheConnectionFactory); | ||||
| 
 | ||||
|         this.valueOps = redisTemplate.opsForValue(); | ||||
|         this.setOps = redisTemplate.opsForSet(); | ||||
| 
 | ||||
|         jsonMapper = new ObjectMapper(); | ||||
|         jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); | ||||
|     } | ||||
| } | ||||
| @ -1,14 +1,16 @@ | ||||
| package com.baeldung.spring.cloud.bootstrap.svcrating.rating; | ||||
| 
 | ||||
| import com.google.common.base.Preconditions; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Optional; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Propagation; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Optional; | ||||
| import com.google.common.base.Preconditions; | ||||
| import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; | ||||
| 
 | ||||
| @Service | ||||
| @Transactional(readOnly = true) | ||||
| @ -17,30 +19,51 @@ public class RatingService { | ||||
|     @Autowired | ||||
|     private RatingRepository ratingRepository; | ||||
| 
 | ||||
|     @Autowired | ||||
|     private RatingCacheRepository cacheRepository; | ||||
| 
 | ||||
|     @HystrixCommand(commandKey = "ratingsByBookIdFromDB", fallbackMethod = "findCachedRatingsByBookId") | ||||
|     public List<Rating> findRatingsByBookId(Long bookId) { | ||||
|         return ratingRepository.findRatingsByBookId(bookId); | ||||
|     } | ||||
| 
 | ||||
|     public List<Rating> findCachedRatingsByBookId(Long bookId) { | ||||
|         return cacheRepository.findCachedRatingsByBookId(bookId); | ||||
|     } | ||||
| 
 | ||||
|     @HystrixCommand(commandKey = "ratingsFromDB", fallbackMethod = "findAllCachedRatings") | ||||
|     public List<Rating> findAllRatings() { | ||||
|         return ratingRepository.findAll(); | ||||
|     } | ||||
| 
 | ||||
|     public List<Rating> findAllCachedRatings() { | ||||
|         return cacheRepository.findAllCachedRatings(); | ||||
|     } | ||||
| 
 | ||||
|     @HystrixCommand(commandKey = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById", ignoreExceptions = { RatingNotFoundException.class }) | ||||
|     public Rating findRatingById(Long ratingId) { | ||||
|         return Optional.ofNullable(ratingRepository.findOne(ratingId)) | ||||
|             .orElseThrow(() -> new RatingNotFoundException("Rating not found. ID: " + ratingId)); | ||||
|     } | ||||
| 
 | ||||
|     public List<Rating> findRatingsByBookId(Long bookId) { | ||||
|         return ratingRepository.findRatingsByBookId(bookId); | ||||
|     } | ||||
| 
 | ||||
|     public List<Rating> findAllRatings() { | ||||
|         return ratingRepository.findAll(); | ||||
|     public Rating findCachedRatingById(Long ratingId) { | ||||
|         return cacheRepository.findCachedRatingById(ratingId); | ||||
|     } | ||||
| 
 | ||||
|     @Transactional(propagation = Propagation.REQUIRED) | ||||
|     public Rating createRating(Rating rating) { | ||||
|         final Rating newRating = new Rating(); | ||||
|         Rating newRating = new Rating(); | ||||
|         newRating.setBookId(rating.getBookId()); | ||||
|         newRating.setStars(rating.getStars()); | ||||
|         return ratingRepository.save(newRating); | ||||
|         Rating persisted = ratingRepository.save(newRating); | ||||
|         cacheRepository.createRating(persisted); | ||||
|         return persisted; | ||||
|     } | ||||
| 
 | ||||
|     @Transactional(propagation = Propagation.REQUIRED) | ||||
|     public void deleteRating(Long ratingId) { | ||||
|         ratingRepository.delete(ratingId); | ||||
|         cacheRepository.deleteRating(ratingId); | ||||
|     } | ||||
| 
 | ||||
|     @Transactional(propagation = Propagation.REQUIRED) | ||||
| @ -54,7 +77,9 @@ public class RatingService { | ||||
|                     break; | ||||
|                 } | ||||
|             }); | ||||
|         return ratingRepository.save(rating); | ||||
|         Rating persisted = ratingRepository.save(rating); | ||||
|         cacheRepository.updateRating(persisted); | ||||
|         return persisted; | ||||
|     } | ||||
| 
 | ||||
|     @Transactional(propagation = Propagation.REQUIRED) | ||||
| @ -64,4 +89,5 @@ public class RatingService { | ||||
|         Preconditions.checkNotNull(ratingRepository.findOne(ratingId)); | ||||
|         return ratingRepository.save(rating); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user