我在 SpringBoot 中有一个控制器,当向端点发出请求时,它会调用 SpringBoot 服务。
该服务使用entityManager 在数据库上执行查询。这些查询可以是任何类型,包括选择和截断,这会修改我的数据库,但我不希望这样。
我希望我的服务具有只读权限。但是,我的应用程序中还有其他服务需要写入权限,因此更改数据库用户对我来说不起作用。
我使用的案例的示例代码是:
@RestController
public class TestController {
@Autowired private TestService testService;
@GetMapping(value = “/test”, produces = "text/plain")
@ResponseBody
public ResponseEntity<String> getResponse(@RequestParam(value = “message”) String message) {
log.info("Received message: {}", message);
List<Map<String, Object>> response = testService.getResults(message);
// ad hoc code
}
}
接下来,我想要对数据库具有只读权限的服务(通过entityManager)
@Service
public class TestService {
@Autowired client<?> client;
@PersistenceContext EntityManager entityManager;
public List<Map<String, Object>> getResults(String userMessage) {
final String query = client.generateQuery(userMessage).toLowerCase();
NativeQueryImpl nativeQuery = (NativeQueryImpl) entityManager.createNativeQuery(query);
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String, Object>> result = nativeQuery.getResultList();
return result;
}
}
只要能达到预期的结果,任何其他方法都是合适的。
我尝试将服务设置为Transactional(readOnly = true),但它只影响性能,而不影响服务权限:
/**
* A boolean flag that can be set to {@code true} if the transaction is
* effectively read-only, allowing for corresponding optimizations at runtime.
* <p>Defaults to {@code false}.
* <p>This just serves as a hint for the actual transaction subsystem;
* it will <i>not necessarily</i> cause failure of write access attempts.
* A transaction manager which cannot interpret the read-only hint will
* <i>not</i> throw an exception when asked for a read-only transaction
* but rather silently ignore the hint.
* @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly()
*/
boolean readOnly() default false;
第一个想法:我认为这通常是不强制执行的。您可以编写一个只执行读取操作的服务,并将其注入到您的控制器中。毕竟,您可以控制自己的代码。
进一步看,您可以将存储库与服务分开。在您的示例中,两者都在一个类中,通常它们是分开的。这将允许您为此目的创建一个只读存储库。
@Service
public class TestService {
@Autowired
private TestReadOnlyRepository testReadOnlyRepository;
public List<TestEntry> getResults(String userMessage) {
return testReadOnlyRepository.findById(userMessage);
}
创建一个与存储库一起使用的实体,而不是列表
@Entity
public class TestEntry {
@Id
@GeneratedValue
private String key;
private String value;
}
注意,我没有使用对象作为值,因为我不知道对象在数据库中是什么列类型。
对于存储库本身,我们可以从 Baeldung 借用来创建一个通用的只读 Spring 数据存储库:
@NoRepositoryBean
public interface ReadOnlyRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
List<T> findAll();
}
public interface TestReadOnlyRepository extends ReadOnlyRepository<TestEntry , String> {
// we only use the methods offered by the ReadOnlyRepository
}